Implement sending friends energy

This commit is contained in:
Melledy
2025-12-22 18:46:21 -08:00
parent b4948212ce
commit c75da823e0
7 changed files with 244 additions and 3 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ Latest nightly compiled server jar: [Nightly](https://nightly.link/Melledy/Nebul
- Daily quests - Daily quests
- Battle pass - Battle pass
- Gacha - Gacha
- Friend system (sending energy not implemented) - Friend system
- Shop (only in-game currency supported) - Shop (only in-game currency supported)
- Commissions - Commissions
- Heartlink - Heartlink
@@ -1,6 +1,7 @@
package emu.nebula.game.friends; package emu.nebula.game.friends;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import emu.nebula.GameConstants; import emu.nebula.GameConstants;
@@ -11,9 +12,12 @@ import emu.nebula.game.player.PlayerManager;
import emu.nebula.net.NetMsgId; import emu.nebula.net.NetMsgId;
import emu.nebula.proto.FriendListGet.FriendListGetResp; import emu.nebula.proto.FriendListGet.FriendListGetResp;
import emu.nebula.proto.Public.FriendDetail; import emu.nebula.proto.Public.FriendDetail;
import emu.nebula.proto.Public.FriendEnergyState;
import emu.nebula.proto.Public.FriendState; import emu.nebula.proto.Public.FriendState;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import lombok.Getter; import lombok.Getter;
import us.hebi.quickbuf.RepeatedLong; import us.hebi.quickbuf.RepeatedLong;
@@ -90,6 +94,12 @@ public class FriendList extends PlayerManager {
return this.getPendingFriends().size() + this.getFriends().size(); return this.getPendingFriends().size() + this.getFriends().size();
} }
public synchronized boolean hasEnergy() {
return this.getFriends().values()
.stream()
.anyMatch(f -> f.getEnergy() > 0);
}
public synchronized Player handleFriendRequest(int targetUid, boolean action) { public synchronized Player handleFriendRequest(int targetUid, boolean action) {
// Make sure we have enough room // Make sure we have enough room
if (this.getFriends().size() >= GameConstants.MAX_FRIENDSHIPS) { if (this.getFriends().size() >= GameConstants.MAX_FRIENDSHIPS) {
@@ -263,6 +273,127 @@ public class FriendList extends PlayerManager {
this.cacheCooldown = 0; this.cacheCooldown = 0;
} }
// Energy
public synchronized LongList sendEnergy(RepeatedLong uids) {
// Get friendships
Collection<Friendship> list = null;
if (uids.length() == 0) {
list = this.getFriends().values();
} else {
list = new ArrayList<>();
for (long uid : uids) {
var friend = this.getFriendById((int) uid);
if (friend == null) {
continue;
}
list.add(friend);
}
}
// Send energy
var results = new LongArrayList();
for (var friend : list) {
// Sanity
if (!friend.canSendEnergy()) {
continue;
}
// Update last send timestamp
friend.updateLastSendEnergy();
friend.save();
// Add to results
results.add(friend.getFriendUid());
// Send energy to friend
Player target = getGameContext().getPlayerModule().getCachedPlayerByUid(friend.getFriendUid());
if (target != null) {
// Get friend's friendship
var theirFriendship = target.getFriendList().getFriendById(this.getPlayer().getUid());
// Send energy
theirFriendship.sendEnergy();
theirFriendship.save();
if (target.isLoaded()) {
// Send message to notify friend that we sent them energy
target.addNextPackage(
NetMsgId.friend_energy_state_notify,
FriendEnergyState.newInstance()
.setState(true)
);
}
}
}
// Clear friend list cache
if (results.size() > 0) {
this.cacheCooldown = 0;
}
// Complete
return results;
}
public synchronized FriendRecvEnergyResult recvEnergy(RepeatedLong uids) {
// Get friendships
Collection<Friendship> list = null;
if (uids.length() == 0) {
list = this.getFriends().values();
} else {
list = new ArrayList<>();
for (long uid : uids) {
var friend = this.getFriendById((int) uid);
if (friend == null) {
continue;
}
list.add(friend);
}
}
// Send energy
var result = new FriendRecvEnergyResult();
for (var friend : list) {
// Skip if we dont have energy
if (friend.getEnergy() <= 0) {
continue;
}
// Add uid
result.getUids().add(friend.getFriendUid());
// Claim energy
result.addEnergy(friend.recvEnergy());
// Save to database
friend.save();
}
// Add energy
if (result.getEnergy() > 0) {
// Add energy to player
this.getPlayer().addEnergy(result.getEnergy(), result.getChange());
// Clear friend list cache
this.cacheCooldown = 0;
}
// Complete
return result;
}
// Database // Database
public synchronized void loadFromDatabase() { public synchronized void loadFromDatabase() {
@@ -305,6 +436,7 @@ public class FriendList extends PlayerManager {
var info = FriendDetail.newInstance() var info = FriendDetail.newInstance()
.setBase(base) .setBase(base)
.setStar(friend.isStar()) .setStar(friend.isStar())
.setSendEnergy(!friend.canSendEnergy())
.setGetEnergy(friend.getEnergy()); .setGetEnergy(friend.getEnergy());
// Add // Add
@@ -0,0 +1,22 @@
package emu.nebula.game.friends;
import emu.nebula.game.player.PlayerChangeInfo;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import lombok.Getter;
@Getter
public class FriendRecvEnergyResult {
private PlayerChangeInfo change;
private LongList uids;
private int energy;
public FriendRecvEnergyResult() {
this.change = new PlayerChangeInfo();
this.uids = new LongArrayList();
}
public void addEnergy(int amount) {
this.energy += amount;
}
}
@@ -21,7 +21,9 @@ public class Friendship implements GameDatabaseObject {
@Setter private boolean isFriend; @Setter private boolean isFriend;
@Setter private boolean star; @Setter private boolean star;
@Setter private int energy;
private int energy;
private long lastSend;
@Setter private transient Player player; @Setter private transient Player player;
@@ -36,6 +38,24 @@ public class Friendship implements GameDatabaseObject {
this.key = Friendship.generateUniqueKey(player.getUid(), friend.getUid()); this.key = Friendship.generateUniqueKey(player.getUid(), friend.getUid());
} }
public boolean canSendEnergy() {
return this.lastSend != this.getPlayer().getLastEpochDay();
}
public void updateLastSendEnergy() {
this.lastSend = this.getPlayer().getLastEpochDay();
}
public void sendEnergy() {
this.energy = 1;
}
public int recvEnergy() {
int amount = this.energy;
this.energy = 0;
return amount;
}
// Database functions // Database functions
public void delete() { public void delete() {
@@ -907,7 +907,8 @@ public class Player implements GameDatabaseObject {
state.getMutableAchievement() state.getMutableAchievement()
.setNew(this.getAchievementManager().hasNewAchievements()); .setNew(this.getAchievementManager().hasNewAchievements());
state.getMutableFriendEnergy(); state.getMutableFriendEnergy()
.setState(this.getFriendList().hasEnergy());
state.getMutableMallPackage(); state.getMutableMallPackage();
state.getMutableTravelerDuelQuest() state.getMutableTravelerDuelQuest()
.setType(QuestType.TravelerDuel); .setType(QuestType.TravelerDuel);
@@ -0,0 +1,34 @@
package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.FriendReceiveEnergy.FriendReceiveEnergyReq;
import emu.nebula.proto.FriendReceiveEnergy.FriendReceiveEnergyResp;
import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.friend_receive_energy_req)
public class HandlerFriendReceiveEnergyReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
// Parse request
var req = FriendReceiveEnergyReq.parseFrom(message);
// Send energy
var result = session.getPlayer().getFriendList().recvEnergy(req.getUIds());
// Build response
var rsp = FriendReceiveEnergyResp.newInstance()
.setChange(result.getChange().toProto())
.setReceiveEnergyCnt(result.getEnergy());
for (long uid : result.getUids()) {
rsp.addUIds(uid);
}
// Encode and send
return session.encodeMsg(NetMsgId.friend_receive_energy_succeed_ack, rsp);
}
}
@@ -0,0 +1,32 @@
package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.FriendSendEnergy.FriendSendEnergyReq;
import emu.nebula.proto.FriendSendEnergy.FriendSendEnergyResp;
import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.friend_send_energy_req)
public class HandlerFriendSendEnergyReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
// Parse request
var req = FriendSendEnergyReq.parseFrom(message);
// Send energy
var results = session.getPlayer().getFriendList().sendEnergy(req.getUIds());
// Build response
var rsp = FriendSendEnergyResp.newInstance();
for (long uid : results) {
rsp.addUIds(uid);
}
// Encode and send
return session.encodeMsg(NetMsgId.friend_send_energy_succeed_ack, rsp);
}
}