mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-14 07:55:57 +01:00
feat: support multiplayer mode in teapot (#2317)
This commit is contained in:
@@ -109,6 +109,7 @@ public class Friendship {
|
||||
.setParam(getFriendProfile().getDaysSinceLogin())
|
||||
.setIsGameSource(true)
|
||||
.setPlatformType(PlatformTypeOuterClass.PlatformType.PLATFORM_TYPE_PC)
|
||||
.setFriendEnterHomeOptionValue(getFriendProfile().getEnterHomeOption())
|
||||
.build();
|
||||
|
||||
return proto;
|
||||
|
||||
@@ -3,8 +3,11 @@ package emu.grasscutter.game.friends;
|
||||
import dev.morphia.annotations.AlsoLoad;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.game.home.GameHome;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.FriendEnterHomeOptionOuterClass;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Entity
|
||||
public class PlayerProfile {
|
||||
@@ -17,11 +20,12 @@ public class PlayerProfile {
|
||||
private int avatarId;
|
||||
private String name;
|
||||
private String signature;
|
||||
private int achievements;
|
||||
|
||||
private int playerLevel;
|
||||
private int worldLevel;
|
||||
private int lastActiveTime;
|
||||
@Getter
|
||||
private int enterHomeOption;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public PlayerProfile() {}
|
||||
@@ -59,10 +63,6 @@ public class PlayerProfile {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public int getAchievements() {
|
||||
return achievements;
|
||||
}
|
||||
|
||||
public int getPlayerLevel() {
|
||||
return playerLevel;
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public class PlayerProfile {
|
||||
this.nameCard = player.getNameCardId();
|
||||
this.playerLevel = player.getLevel();
|
||||
this.worldLevel = player.getWorldLevel();
|
||||
// this.achievements = 0;
|
||||
this.enterHomeOption = player.tryGetHome().map(GameHome::getEnterHomeOption).orElse(FriendEnterHomeOptionOuterClass.FriendEnterHomeOption.FRIEND_ENTER_HOME_OPTION_REFUSE_VALUE);
|
||||
this.updateLastActiveTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package emu.grasscutter.game.home;
|
||||
|
||||
import emu.grasscutter.game.CoopRequest;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
public class EnterHomeRequest extends CoopRequest {
|
||||
public EnterHomeRequest(Player requester) {
|
||||
super(requester);
|
||||
}
|
||||
}
|
||||
@@ -4,31 +4,40 @@ import dev.morphia.annotations.*;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.HomeWorldLevelData;
|
||||
import emu.grasscutter.data.excels.scene.SceneData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Entity(value = "homes", useDiscriminator = false)
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public class GameHome {
|
||||
@Id String id;
|
||||
public static final Set<Integer> HOME_SCENE_IDS = GameData.getSceneDataMap().values().stream()
|
||||
.filter(sceneData -> sceneData.getSceneType() == SceneType.SCENE_HOME_WORLD || sceneData.getSceneType() == SceneType.SCENE_HOME_ROOM)
|
||||
.map(SceneData::getId).collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
@Id
|
||||
String id;
|
||||
|
||||
@Indexed(options = @IndexOptions(unique = true))
|
||||
long ownerUid;
|
||||
|
||||
@Transient Player player;
|
||||
@Transient
|
||||
Player player;
|
||||
|
||||
int level;
|
||||
int exp;
|
||||
@@ -49,13 +58,17 @@ public class GameHome {
|
||||
return home;
|
||||
}
|
||||
|
||||
public static boolean doesHomeExist(int uid) {
|
||||
return DatabaseHelper.getHomeByUid(uid) != null;
|
||||
}
|
||||
|
||||
public static GameHome create(Integer uid) {
|
||||
return GameHome.of()
|
||||
.ownerUid(uid)
|
||||
.level(1)
|
||||
.sceneMap(new ConcurrentHashMap<>())
|
||||
.unlockedHomeBgmList(new HashSet<>())
|
||||
.build();
|
||||
.ownerUid(uid)
|
||||
.level(1)
|
||||
.sceneMap(new ConcurrentHashMap<>())
|
||||
.unlockedHomeBgmList(new HashSet<>())
|
||||
.build();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
@@ -64,19 +77,19 @@ public class GameHome {
|
||||
|
||||
public HomeSceneItem getHomeSceneItem(int sceneId) {
|
||||
return sceneMap.computeIfAbsent(
|
||||
sceneId,
|
||||
e -> {
|
||||
var defaultItem = GameData.getHomeworldDefaultSaveData().get(sceneId);
|
||||
if (defaultItem != null) {
|
||||
Grasscutter.getLogger()
|
||||
.info("Set player {} home {} to initial setting", ownerUid, sceneId);
|
||||
return HomeSceneItem.parseFrom(defaultItem, sceneId);
|
||||
} else {
|
||||
// Realm res missing bricks account, use default realm data to allow main house
|
||||
defaultItem = GameData.getHomeworldDefaultSaveData().get(2001);
|
||||
return HomeSceneItem.parseFrom(defaultItem, sceneId);
|
||||
}
|
||||
});
|
||||
sceneId,
|
||||
e -> {
|
||||
var defaultItem = GameData.getHomeworldDefaultSaveData().get(sceneId);
|
||||
if (defaultItem != null) {
|
||||
Grasscutter.getLogger()
|
||||
.info("Set player {} home {} to initial setting", ownerUid, sceneId);
|
||||
return HomeSceneItem.parseFrom(defaultItem, sceneId);
|
||||
} else {
|
||||
// Realm res missing bricks account, use default realm data to allow main house
|
||||
defaultItem = GameData.getHomeworldDefaultSaveData().get(2001);
|
||||
return HomeSceneItem.parseFrom(defaultItem, sceneId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onOwnerLogin(Player player) {
|
||||
@@ -130,9 +143,9 @@ public class GameHome {
|
||||
|
||||
private Set<Integer> getDefaultUnlockedHomeBgmIds() {
|
||||
return GameData.getHomeWorldBgmDataMap().int2ObjectEntrySet().stream()
|
||||
.filter(e -> e.getValue().isDefaultUnlock())
|
||||
.map(Int2ObjectMap.Entry::getIntKey)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
.filter(e -> e.getValue().isDefaultUnlock())
|
||||
.map(Int2ObjectMap.Entry::getIntKey)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
// Same as Player.java addExpDirectly
|
||||
@@ -171,7 +184,7 @@ public class GameHome {
|
||||
|
||||
// Ensure next update is at top of the hour
|
||||
nextUpdateTime =
|
||||
(int) ZonedDateTime.now().plusHours(1).truncatedTo(ChronoUnit.HOURS).toEpochSecond();
|
||||
(int) ZonedDateTime.now().plusHours(1).truncatedTo(ChronoUnit.HOURS).toEpochSecond();
|
||||
|
||||
// Get resources
|
||||
var hourlyResources = getComfortResources(player);
|
||||
@@ -194,42 +207,42 @@ public class GameHome {
|
||||
|
||||
// Outdoors avatars
|
||||
sceneMap
|
||||
.get(player.getCurrentRealmId() + 2000)
|
||||
.getBlockItems()
|
||||
.forEach(
|
||||
(i, e) -> {
|
||||
e.getDeployNPCList()
|
||||
.forEach(
|
||||
id -> {
|
||||
invitedAvatars.add(id.getAvatarId());
|
||||
});
|
||||
});
|
||||
.get(player.getCurrentRealmId() + 2000)
|
||||
.getBlockItems()
|
||||
.forEach(
|
||||
(i, e) -> {
|
||||
e.getDeployNPCList()
|
||||
.forEach(
|
||||
id -> {
|
||||
invitedAvatars.add(id.getAvatarId());
|
||||
});
|
||||
});
|
||||
|
||||
// Check as realm 5 inside is not in defaults and will be null
|
||||
if (Objects.nonNull(sceneMap.get(player.getCurrentRealmId() + 2200))) {
|
||||
// Indoors avatars
|
||||
sceneMap
|
||||
.get(player.getCurrentRealmId() + 2200)
|
||||
.getBlockItems()
|
||||
.forEach(
|
||||
(i, e) -> {
|
||||
e.getDeployNPCList()
|
||||
.forEach(
|
||||
id -> {
|
||||
invitedAvatars.add(id.getAvatarId());
|
||||
});
|
||||
});
|
||||
.get(player.getCurrentRealmId() + 2200)
|
||||
.getBlockItems()
|
||||
.forEach(
|
||||
(i, e) -> {
|
||||
e.getDeployNPCList()
|
||||
.forEach(
|
||||
id -> {
|
||||
invitedAvatars.add(id.getAvatarId());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Add exp to all avatars
|
||||
invitedAvatars.forEach(
|
||||
id -> {
|
||||
var avatar = player.getAvatars().getAvatarById(id);
|
||||
player
|
||||
.getServer()
|
||||
.getInventorySystem()
|
||||
.upgradeAvatarFetterLevel(player, avatar, storedFetterExp);
|
||||
});
|
||||
id -> {
|
||||
var avatar = player.getAvatars().getAvatarById(id);
|
||||
player
|
||||
.getServer()
|
||||
.getInventorySystem()
|
||||
.upgradeAvatarFetterLevel(player, avatar, storedFetterExp);
|
||||
});
|
||||
|
||||
storedFetterExp = 0;
|
||||
save();
|
||||
@@ -253,7 +266,7 @@ public class GameHome {
|
||||
storeResources(player, 0, 0);
|
||||
lastUpdatedTime = clientTime;
|
||||
nextUpdateTime =
|
||||
(int) ZonedDateTime.now().plusHours(1).truncatedTo(ChronoUnit.HOURS).toEpochSecond();
|
||||
(int) ZonedDateTime.now().plusHours(1).truncatedTo(ChronoUnit.HOURS).toEpochSecond();
|
||||
save();
|
||||
|
||||
// Send packet
|
||||
|
||||
162
src/main/java/emu/grasscutter/game/home/HomeWorld.java
Normal file
162
src/main/java/emu/grasscutter/game/home/HomeWorld.java
Normal file
@@ -0,0 +1,162 @@
|
||||
package emu.grasscutter.game.home;
|
||||
|
||||
import emu.grasscutter.game.entity.EntityTeam;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.proto.ChatInfoOuterClass;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HomeWorld extends World {
|
||||
@Getter
|
||||
private final GameHome home;
|
||||
|
||||
public HomeWorld(GameServer server, Player owner) {
|
||||
super(server, owner);
|
||||
|
||||
this.home = owner.isOnline() ? owner.getHome() : GameHome.getByUid(owner.getUid());
|
||||
server.registerHomeWorld(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addPlayer(Player player) {
|
||||
// Check if player already in
|
||||
if (this.getPlayers().contains(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove player from prev world
|
||||
if (player.getWorld() != null) {
|
||||
player.getWorld().removePlayer(player);
|
||||
}
|
||||
|
||||
// Register
|
||||
player.setWorld(this);
|
||||
this.getPlayers().add(player);
|
||||
|
||||
// Set player variables
|
||||
if (this.getHost().equals(player)) {
|
||||
player.setPeerId(1);
|
||||
this.getGuests().forEach(player1 -> player1.setPeerId(player1.getPeerId() + 1));
|
||||
} else {
|
||||
player.setPeerId(this.getNextPeerId());
|
||||
}
|
||||
|
||||
player.getTeamManager().setEntity(new EntityTeam(player));
|
||||
|
||||
// Copy main team to multiplayer team
|
||||
if (this.isMultiplayer()) {
|
||||
player
|
||||
.getTeamManager()
|
||||
.getMpTeam()
|
||||
.copyFrom(
|
||||
player.getTeamManager().getCurrentSinglePlayerTeamInfo(),
|
||||
player.getTeamManager().getMaxTeamSize());
|
||||
player.getTeamManager().setCurrentCharacterIndex(0);
|
||||
|
||||
if (!player.equals(this.getHost())) {
|
||||
this.broadcastPacket(
|
||||
new PacketPlayerChatNotify(
|
||||
player,
|
||||
0,
|
||||
ChatInfoOuterClass.ChatInfo.SystemHint.newBuilder()
|
||||
.setType(ChatInfoOuterClass.ChatInfo.SystemHintType.SYSTEM_HINT_TYPE_CHAT_ENTER_WORLD.getNumber())
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
// Add to scene
|
||||
var scene = this.getSceneById(player.getSceneId());
|
||||
scene.addPlayer(player);
|
||||
|
||||
// Info packet for other players
|
||||
if (this.getPlayers().size() > 1) {
|
||||
this.updatePlayerInfos(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void removePlayer(Player player) {
|
||||
// Remove team entities
|
||||
this.broadcastPacket(
|
||||
new PacketDelTeamEntityNotify(
|
||||
player.getSceneId(),
|
||||
this.getPlayers().stream()
|
||||
.map(
|
||||
p ->
|
||||
p.getTeamManager().getEntity() == null
|
||||
? 0
|
||||
: p.getTeamManager().getEntity().getId())
|
||||
.toList()));
|
||||
|
||||
// Deregister
|
||||
this.getPlayers().remove(player);
|
||||
player.setWorld(null);
|
||||
|
||||
// Remove from scene
|
||||
Scene scene = this.getSceneById(player.getSceneId());
|
||||
scene.removePlayer(player);
|
||||
|
||||
// Info packet for other players
|
||||
if (this.getPlayers().size() > 0) {
|
||||
this.updatePlayerInfos(player);
|
||||
}
|
||||
|
||||
this.broadcastPacket(
|
||||
new PacketPlayerChatNotify(
|
||||
player,
|
||||
0,
|
||||
ChatInfoOuterClass.ChatInfo.SystemHint.newBuilder()
|
||||
.setType(ChatInfoOuterClass.ChatInfo.SystemHintType.SYSTEM_HINT_TYPE_CHAT_LEAVE_WORLD.getNumber())
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextPeerId() {
|
||||
return this.getPlayers().size() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setHost(Player host) {
|
||||
super.setHost(host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isMultiplayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isPaused() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isTimeLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getOwnerUid() {
|
||||
return this.getHost().getUid();
|
||||
}
|
||||
|
||||
public List<Player> getGuests() {
|
||||
return this.getPlayers().stream().filter(player -> !player.equals(this.getHost())).toList();
|
||||
}
|
||||
|
||||
public boolean isInHome(Player player) {
|
||||
return this.getPlayers().contains(player);
|
||||
}
|
||||
|
||||
public void sendPacketToHostIfOnline(BasePacket basePacket) {
|
||||
if (this.getHost().isOnline()) {
|
||||
this.getHost().sendPacket(basePacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
187
src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java
Normal file
187
src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java
Normal file
@@ -0,0 +1,187 @@
|
||||
package emu.grasscutter.game.home;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.EnterReason;
|
||||
import emu.grasscutter.game.world.Position;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.game.world.data.TeleportProperties;
|
||||
import emu.grasscutter.net.proto.EnterTypeOuterClass;
|
||||
import emu.grasscutter.net.proto.OtherPlayerEnterHomeNotifyOuterClass;
|
||||
import emu.grasscutter.net.proto.PlayerApplyEnterHomeResultNotifyOuterClass;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||
import emu.grasscutter.server.event.player.PlayerEnterHomeEvent;
|
||||
import emu.grasscutter.server.event.player.PlayerLeaveHomeEvent;
|
||||
import emu.grasscutter.server.event.player.PlayerTeleportEvent;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
|
||||
public class HomeWorldMPSystem extends BaseGameSystem {
|
||||
public HomeWorldMPSystem(GameServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
public void sendEnterHomeRequest(Player requester, int ownerUid) {
|
||||
var owner = getServer().getPlayerByUid(ownerUid);
|
||||
if (owner == null) {
|
||||
requester.sendPacket(new PacketPlayerApplyEnterHomeResultNotify(ownerUid, "", false, PlayerApplyEnterHomeResultNotifyOuterClass.PlayerApplyEnterHomeResultNotify.Reason.OPEN_STATE_NOT_OPEN));
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner.getRealmList() == null) {
|
||||
requester.sendPacket(new PacketPlayerApplyEnterHomeResultNotify(ownerUid, "", false, PlayerApplyEnterHomeResultNotifyOuterClass.PlayerApplyEnterHomeResultNotify.Reason.OPEN_STATE_NOT_OPEN));
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
var request = owner.getEnterHomeRequests().get(requester.getUid());
|
||||
|
||||
if (request != null && !request.isExpired()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner.isInEditMode()) {
|
||||
requester.sendPacket(new PacketPlayerApplyEnterHomeResultNotify(ownerUid, owner.getNickname(), false, PlayerApplyEnterHomeResultNotifyOuterClass.PlayerApplyEnterHomeResultNotify.Reason.HOST_IN_EDIT_MODE));
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
request = new EnterHomeRequest(requester);
|
||||
owner.getEnterHomeRequests().put(requester.getUid(), request);
|
||||
|
||||
owner.sendPacket(new PacketPlayerApplyEnterHomeNotify(requester));
|
||||
}
|
||||
|
||||
public void acceptEnterHomeRequest(Player owner, int requesterUid, boolean isAgreed) {
|
||||
var request = owner.getEnterHomeRequests().get(requesterUid);
|
||||
if (request == null || request.isExpired()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var requester = request.getRequester();
|
||||
owner.getEnterHomeRequests().remove(requesterUid);
|
||||
|
||||
if (requester.getWorld().isMultiplayer()) {
|
||||
requester.sendPacket(new PacketPlayerApplyEnterHomeResultNotify(owner.getUid(), owner.getNickname(), false, PlayerApplyEnterHomeResultNotifyOuterClass.PlayerApplyEnterHomeResultNotify.Reason.SYSTEM_JUDGE));
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
requester.sendPacket(new PacketPlayerApplyEnterHomeResultNotify(owner.getUid(), owner.getNickname(), isAgreed, PlayerApplyEnterHomeResultNotifyOuterClass.PlayerApplyEnterHomeResultNotify.Reason.PLAYER_JUDGE));
|
||||
|
||||
if (!isAgreed) {
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
this.enterHome(requester, owner);
|
||||
}
|
||||
|
||||
public void enterHome(Player requester, Player owner) {
|
||||
if (requester.getWorld().isMultiplayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner.getRealmList() == null) {
|
||||
// should never happen
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp(RetcodeOuterClass.Retcode.RET_HOME_NOT_FOUND_IN_MEM_VALUE, owner.getUid()));
|
||||
return;
|
||||
}
|
||||
|
||||
var world = this.server.getHomeWorldOrCreate(owner);
|
||||
var targetHome = world.getHome();
|
||||
|
||||
var event = new PlayerEnterHomeEvent(requester, owner, targetHome);
|
||||
event.call();
|
||||
if (event.isCanceled()) {
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp(RetcodeOuterClass.Retcode.RET_HOME_OWNER_REFUSE_TO_ENTER_HOME_VALUE, owner.getUid()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner.isInEditMode()) {
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp(RetcodeOuterClass.Retcode.RET_HOME_CANT_ENTER_BY_IN_EDIT_MODE_VALUE, owner.getUid()));
|
||||
return;
|
||||
}
|
||||
|
||||
int realmId = 2000 + owner.getCurrentRealmId();
|
||||
targetHome.getHomeSceneItem(realmId);
|
||||
targetHome.save();
|
||||
var pos = world.getSceneById(realmId).getScriptManager().getConfig().born_pos;
|
||||
|
||||
requester.getPrevPosForHome().set(requester.getPosition());
|
||||
requester.setCurHomeWorld(world);
|
||||
requester.setPrevScene(requester.getSceneId());
|
||||
world.addPlayer(requester, realmId);
|
||||
requester.setSceneId(realmId);
|
||||
requester.getPosition().set(pos);
|
||||
|
||||
requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, owner.getUid(), TeleportProperties.builder().sceneId(realmId).enterReason(EnterReason.EnterHome).teleportTo(pos).teleportType(PlayerTeleportEvent.TeleportType.INTERNAL).build(), !requester.equals(owner)));
|
||||
requester.sendPacket(new PacketTryEnterHomeRsp(owner.getUid()));
|
||||
|
||||
requester.setHasSentInitPacketInHome(false);
|
||||
world.getPlayers().stream()
|
||||
.filter(player -> !player.equals(requester))
|
||||
.forEach(player -> player.sendPacket(new PacketPlayerPreEnterMpNotify(requester)));
|
||||
}
|
||||
|
||||
public boolean leaveCoop(Player player, int prevScene) {
|
||||
return this.leaveCoop(player, prevScene, player.getPrevPosForHome());
|
||||
}
|
||||
|
||||
public boolean leaveCoop(Player player, int prevScene, Position pos) {
|
||||
// Make sure everyone's scene is loaded
|
||||
for (var p : player.getWorld().getPlayers()) {
|
||||
if (p.getSceneLoadState() != Player.SceneLoadState.LOADED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Event
|
||||
var event = new PlayerLeaveHomeEvent(player, player.getCurHomeWorld().getHost(), player.getCurHomeWorld().getHome(), PlayerLeaveHomeEvent.Reason.PLAYER_LEAVE);
|
||||
event.call();
|
||||
|
||||
player.getPosition().set(pos);
|
||||
var world = new World(player);
|
||||
world.addPlayer(player, prevScene);
|
||||
player.getCurHomeWorld().sendPacketToHostIfOnline(new PacketOtherPlayerEnterOrLeaveHomeNotify(player, OtherPlayerEnterHomeNotifyOuterClass.OtherPlayerEnterHomeNotify.Reason.LEAVE));
|
||||
player.setCurHomeWorld(this.server.getHomeWorldOrCreate(player));
|
||||
|
||||
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterTypeOuterClass.EnterType.ENTER_TYPE_BACK, EnterReason.TeamBack, prevScene, pos));
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean kickPlayerFromHome(Player owner, int targetUid) {
|
||||
// Make sure player's world is multiplayer and that player is owner
|
||||
if (!owner.getCurHomeWorld().getHost().equals(owner)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get victim and sanity checks
|
||||
var victim = owner.getServer().getPlayerByUid(targetUid);
|
||||
if (victim == null || owner.equals(victim)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure victim's scene has loaded
|
||||
if (victim.getSceneLoadState() != Player.SceneLoadState.LOADED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Event
|
||||
var event = new PlayerLeaveHomeEvent(victim, owner, victim.getCurHomeWorld().getHome(), PlayerLeaveHomeEvent.Reason.KICKED);
|
||||
event.call();
|
||||
|
||||
// Kick
|
||||
victim.getPosition().set(victim.getPrevPosForHome());
|
||||
var world = new World(victim);
|
||||
world.addPlayer(victim, 3);
|
||||
victim.getCurHomeWorld().sendPacketToHostIfOnline(new PacketOtherPlayerEnterOrLeaveHomeNotify(victim, OtherPlayerEnterHomeNotifyOuterClass.OtherPlayerEnterHomeNotify.Reason.LEAVE));
|
||||
victim.setCurHomeWorld(this.server.getHomeWorldOrCreate(victim));
|
||||
|
||||
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterTypeOuterClass.EnterType.ENTER_TYPE_BACK, EnterReason.TeamKick, victim.getScene().getId(), victim.getPrevPosForHome()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import emu.grasscutter.game.expedition.ExpeditionInfo;
|
||||
import emu.grasscutter.game.friends.FriendsList;
|
||||
import emu.grasscutter.game.friends.PlayerProfile;
|
||||
import emu.grasscutter.game.gacha.PlayerGachaInfo;
|
||||
import emu.grasscutter.game.home.EnterHomeRequest;
|
||||
import emu.grasscutter.game.home.GameHome;
|
||||
import emu.grasscutter.game.home.HomeWorld;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.Inventory;
|
||||
import emu.grasscutter.game.mail.Mail;
|
||||
@@ -29,6 +31,7 @@ import emu.grasscutter.game.mail.MailHandler;
|
||||
import emu.grasscutter.game.managers.FurnitureManager;
|
||||
import emu.grasscutter.game.managers.ResinManager;
|
||||
import emu.grasscutter.game.managers.SatiationManager;
|
||||
import emu.grasscutter.game.managers.SotSManager;
|
||||
import emu.grasscutter.game.managers.cooking.ActiveCookCompoundData;
|
||||
import emu.grasscutter.game.managers.cooking.CookingCompoundManager;
|
||||
import emu.grasscutter.game.managers.cooking.CookingManager;
|
||||
@@ -38,7 +41,6 @@ import emu.grasscutter.game.managers.forging.ActiveForgeData;
|
||||
import emu.grasscutter.game.managers.forging.ForgingManager;
|
||||
import emu.grasscutter.game.managers.mapmark.MapMark;
|
||||
import emu.grasscutter.game.managers.mapmark.MapMarksManager;
|
||||
import emu.grasscutter.game.managers.SotSManager;
|
||||
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
||||
import emu.grasscutter.game.props.*;
|
||||
import emu.grasscutter.game.quest.QuestManager;
|
||||
@@ -58,15 +60,13 @@ import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
||||
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
|
||||
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass;
|
||||
import emu.grasscutter.net.proto.*;
|
||||
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
|
||||
import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||
import emu.grasscutter.net.proto.SocialShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.plugin.api.PlayerHook;
|
||||
import emu.grasscutter.scripts.ScriptLoader;
|
||||
import emu.grasscutter.scripts.data.SceneRegion;
|
||||
import emu.grasscutter.server.event.player.PlayerEnterAreaEvent;
|
||||
import emu.grasscutter.server.event.player.PlayerJoinEvent;
|
||||
@@ -75,7 +75,8 @@ import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.game.GameSession.SessionState;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.*;
|
||||
import emu.grasscutter.utils.DispatchUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import emu.grasscutter.utils.helpers.DateHelper;
|
||||
import emu.grasscutter.utils.objects.FieldFetch;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
@@ -108,6 +109,8 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
@Getter private int nameCardId = 210001;
|
||||
@Getter private Position position;
|
||||
@Getter @Setter private Position prevPos;
|
||||
@Getter @Setter private Position prevPosForHome;
|
||||
@Getter @Setter private int prevScene;
|
||||
@Getter private Position rotation;
|
||||
@Getter private PlayerBirthday birthday;
|
||||
@Getter private PlayerCodex codex;
|
||||
@@ -116,6 +119,7 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
@Getter @Setter private List<Integer> showNameCardList;
|
||||
@Getter private Map<Integer, Integer> properties;
|
||||
@Getter @Setter private int currentRealmId;
|
||||
@Getter @Setter private transient boolean isInEditMode;
|
||||
@Getter @Setter private int widgetId;
|
||||
@Getter @Setter private int sceneId;
|
||||
@Getter @Setter private int regionId;
|
||||
@@ -148,6 +152,8 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
@Transient private long nextGuid = 0;
|
||||
@Transient @Getter @Setter private int peerId;
|
||||
@Transient private World world; // Synchronized getter and setter
|
||||
@Transient @Getter @Setter private HomeWorld curHomeWorld;
|
||||
@Transient @Getter @Setter private boolean hasSentInitPacketInHome;
|
||||
@Transient private Scene scene; // Synchronized getter and setter
|
||||
@Transient @Getter private int weatherId = 0;
|
||||
@Transient @Getter private ClimateType climate = ClimateType.CLIMATE_SUNNY;
|
||||
@@ -202,6 +208,7 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
@Transient @Getter @Setter private SceneLoadState sceneLoadState = SceneLoadState.NONE;
|
||||
@Transient private boolean hasSentLoginPackets;
|
||||
@Transient private long nextSendPlayerLocTime = 0;
|
||||
@Getter private transient final Int2ObjectMap<EnterHomeRequest> enterHomeRequests;
|
||||
|
||||
private transient final Int2ObjectMap<CoopRequest> coopRequests; // Synchronized getter
|
||||
@Getter private transient final Queue<AttackResult> attackResults;
|
||||
@@ -238,6 +245,7 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
this.buffManager = new PlayerBuffManager(this);
|
||||
this.position = new Position(GameConstants.START_POSITION);
|
||||
this.prevPos = new Position();
|
||||
this.prevPosForHome = Position.ZERO;
|
||||
this.rotation = new Position(0, 307, 0);
|
||||
this.sceneId = 3;
|
||||
this.regionId = 1;
|
||||
@@ -274,6 +282,7 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
|
||||
this.attackResults = new LinkedBlockingQueue<>();
|
||||
this.coopRequests = new Int2ObjectOpenHashMap<>();
|
||||
this.enterHomeRequests = new Int2ObjectOpenHashMap<>();
|
||||
this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class);
|
||||
this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class);
|
||||
this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class);
|
||||
@@ -494,6 +503,18 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
this.seenRealmList.add(seenId);
|
||||
}
|
||||
|
||||
public Optional<GameHome> tryGetHome() {
|
||||
if (this.isOnline()) {
|
||||
return Optional.ofNullable(this.home);
|
||||
}
|
||||
|
||||
if (GameHome.doesHomeExist(this.getUid())) {
|
||||
this.home = GameHome.getByUid(this.getUid());
|
||||
}
|
||||
|
||||
return Optional.ofNullable(this.home);
|
||||
}
|
||||
|
||||
public int getExpeditionLimit() {
|
||||
final int CONST_VALUE_EXPEDITION_INIT_LIMIT = 2; // TODO: pull from ConstValueExcelConfigData.json
|
||||
int expeditionLimit = CONST_VALUE_EXPEDITION_INIT_LIMIT;
|
||||
@@ -716,7 +737,7 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
this.getQuestManager().forEachActiveQuest(quest -> {
|
||||
if (quest.getTriggerData() != null &&
|
||||
quest.getTriggers().containsKey(enterRegionName) &&
|
||||
region.getGroupId() == quest.getTriggerData().get(enterRegionName).getGroupId()) {
|
||||
region.getGroupId() == quest.getTriggerData().get(enterRegionName).getGroupId()) {
|
||||
// If trigger hasn't been fired yet
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put(enterRegionName, true))) {
|
||||
this.getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||
@@ -1201,6 +1222,21 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean expireEnterHomeRequest(EnterHomeRequest req) {
|
||||
return this.expireEnterHomeRequest(req, false);
|
||||
}
|
||||
|
||||
private boolean expireEnterHomeRequest(EnterHomeRequest req, boolean force) {
|
||||
if (!req.isExpired() && !force) return false;
|
||||
req.getRequester().sendPacket(new PacketPlayerApplyEnterHomeResultNotify(
|
||||
this.getUid(),
|
||||
this.getNickname(),
|
||||
false,
|
||||
PlayerApplyEnterHomeResultNotifyOuterClass.PlayerApplyEnterHomeResultNotify.Reason.SYSTEM_JUDGE));
|
||||
req.getRequester().sendPacket(new PacketTryEnterHomeRsp());
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized void onTick() {
|
||||
// Check ping
|
||||
if (this.getLastPingTime() > System.currentTimeMillis() + 60000) {
|
||||
@@ -1209,6 +1245,8 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
}
|
||||
// Check co-op requests
|
||||
this.getCoopRequests().values().removeIf(this::expireCoopRequest);
|
||||
// Check enter-home requests
|
||||
this.getEnterHomeRequests().values().removeIf(this::expireEnterHomeRequest);
|
||||
// Handle buff
|
||||
this.getBuffManager().onTick();
|
||||
// Ping
|
||||
@@ -1322,9 +1360,9 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
}
|
||||
|
||||
// Load from db
|
||||
var runner = Grasscutter.getThreadPool();
|
||||
runner.submit(() -> this.achievements = Achievements.getByPlayer(this));
|
||||
this.achievements = Achievements.getByPlayer(this);
|
||||
|
||||
var runner = Grasscutter.getThreadPool();
|
||||
runner.submit(this.getAvatars()::loadFromDatabase);
|
||||
runner.submit(this.getInventory()::loadFromDatabase);
|
||||
|
||||
@@ -1365,6 +1403,16 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
if (GameHome.HOME_SCENE_IDS.contains(this.getSceneId())) {
|
||||
this.setSceneId(this.prevScene <= 0 ? 3 : this.prevScene); // if the player in home, make the player go back.
|
||||
var pos = this.getPrevPosForHome();
|
||||
if (pos.equals(Position.ZERO)) {
|
||||
pos = ScriptLoader.getSceneMeta(this.getSceneId()).config.born_pos;
|
||||
}
|
||||
this.position.set(pos);
|
||||
}
|
||||
|
||||
// Create world
|
||||
World world = new World(this);
|
||||
world.addPlayer(this);
|
||||
@@ -1413,7 +1461,10 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
|
||||
this.furnitureManager.onLogin();
|
||||
// Home
|
||||
home = GameHome.getByUid(getUid());
|
||||
var homeWorld = this.getServer().getHomeWorldOrCreate(this);
|
||||
homeWorld.setHost(this); // synchronize player object if homeWorld already exists in the server.
|
||||
this.home = homeWorld.getHome();
|
||||
this.setCurHomeWorld(homeWorld);
|
||||
home.onOwnerLogin(this);
|
||||
// Activity
|
||||
this.activityManager = new ActivityManager(this);
|
||||
@@ -1461,6 +1512,8 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
this.getProfile().setPlayer(null); // Set offline
|
||||
|
||||
this.getCoopRequests().clear();
|
||||
this.getEnterHomeRequests().values().forEach(req -> this.expireEnterHomeRequest(req, true));
|
||||
this.getEnterHomeRequests().clear();
|
||||
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
@@ -14,6 +14,7 @@ public class ItemUseUnlockHomeModule extends ItemUseInt {
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return false;
|
||||
params.player.addRealmList(this.i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,11 @@ public class MultiplayerSystem extends BaseGameSystem {
|
||||
}
|
||||
|
||||
public boolean leaveCoop(Player player) {
|
||||
// Make sure player is not in home
|
||||
if (player.getCurHomeWorld().isInHome(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure player's world is multiplayer
|
||||
if (!player.getWorld().isMultiplayer()) {
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package emu.grasscutter.game.world;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.*;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameDepot;
|
||||
import emu.grasscutter.data.binout.SceneNpcBornEntry;
|
||||
import emu.grasscutter.data.binout.routes.Route;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
@@ -11,35 +12,44 @@ import emu.grasscutter.data.excels.scene.SceneData;
|
||||
import emu.grasscutter.data.excels.world.WorldLevelData;
|
||||
import emu.grasscutter.data.server.Grid;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.dungeons.*;
|
||||
import emu.grasscutter.game.dungeons.DungeonManager;
|
||||
import emu.grasscutter.game.dungeons.DungeonSettleListener;
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
import emu.grasscutter.game.entity.*;
|
||||
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.managers.blossom.BlossomManager;
|
||||
import emu.grasscutter.game.player.*;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.player.TeamInfo;
|
||||
import emu.grasscutter.game.props.*;
|
||||
import emu.grasscutter.game.quest.QuestGroupSuite;
|
||||
import emu.grasscutter.game.world.data.TeleportProperties;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||
import emu.grasscutter.net.proto.*;
|
||||
import emu.grasscutter.net.proto.EnterTypeOuterClass;
|
||||
import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass;
|
||||
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
|
||||
import emu.grasscutter.scripts.*;
|
||||
import emu.grasscutter.scripts.SceneIndexManager;
|
||||
import emu.grasscutter.scripts.SceneScriptManager;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.*;
|
||||
import emu.grasscutter.scripts.data.SceneBlock;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||
import emu.grasscutter.server.event.entity.EntityCreationEvent;
|
||||
import emu.grasscutter.server.event.player.PlayerTeleportEvent;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.server.scheduler.ServerTaskScheduler;
|
||||
import emu.grasscutter.utils.objects.KahnsSort;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import lombok.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class Scene {
|
||||
@@ -260,6 +270,13 @@ public final class Scene {
|
||||
this.removeEntity(gadget);
|
||||
}
|
||||
|
||||
// Remove player widget gadgets
|
||||
this.getEntities().values().stream()
|
||||
.filter(gameEntity -> gameEntity instanceof EntityVehicle)
|
||||
.map(gameEntity -> (EntityVehicle) gameEntity)
|
||||
.filter(entityVehicle -> entityVehicle.getOwner().equals(player))
|
||||
.forEach(entityVehicle -> this.removeEntity(entityVehicle, VisionType.VISION_TYPE_REMOVE));
|
||||
|
||||
// Deregister scene if not in use
|
||||
if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty) {
|
||||
this.getScriptManager().onDestroy();
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
package emu.grasscutter.game.world;
|
||||
|
||||
import static emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType.SCRIPT;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonData;
|
||||
import emu.grasscutter.game.entity.*;
|
||||
import emu.grasscutter.game.entity.EntityTeam;
|
||||
import emu.grasscutter.game.entity.EntityWorld;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.player.Player.SceneLoadState;
|
||||
import emu.grasscutter.game.props.*;
|
||||
import emu.grasscutter.game.props.EnterReason;
|
||||
import emu.grasscutter.game.props.EntityIdType;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.world.data.TeleportProperties;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo.*;
|
||||
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo.SystemHint;
|
||||
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo.SystemHintType;
|
||||
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
|
||||
import emu.grasscutter.scripts.data.SceneConfig;
|
||||
import emu.grasscutter.server.event.player.PlayerTeleportEvent;
|
||||
@@ -19,14 +22,23 @@ import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.ConversionUtils;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import java.util.*;
|
||||
import lombok.*;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType.SCRIPT;
|
||||
|
||||
public class World implements Iterable<Player> {
|
||||
@Getter private final GameServer server;
|
||||
@Getter private final Player host;
|
||||
@Getter private Player host;
|
||||
@Getter private final List<Player> players;
|
||||
@Getter private final Int2ObjectMap<Scene> scenes;
|
||||
|
||||
@@ -65,6 +77,15 @@ public class World implements Iterable<Player> {
|
||||
this.host.getServer().registerWorld(this);
|
||||
}
|
||||
|
||||
public World(GameServer server, Player owner) {
|
||||
this.server = server;
|
||||
this.host = owner;
|
||||
this.players = Collections.synchronizedList(new ArrayList<>());
|
||||
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
|
||||
this.entity = new EntityWorld(this);
|
||||
this.lastUpdateTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public int getLevelEntityId() {
|
||||
return entity.getId();
|
||||
}
|
||||
@@ -90,6 +111,10 @@ public class World implements Iterable<Player> {
|
||||
this.worldLevel = worldLevel;
|
||||
}
|
||||
|
||||
protected synchronized void setHost(Player host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an associated scene by ID. Creates a new instance of the scene if it doesn't exist.
|
||||
*
|
||||
@@ -179,6 +204,58 @@ public class World implements Iterable<Player> {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void addPlayer(Player player, int newSceneId) {
|
||||
// Check if player already in
|
||||
if (this.getPlayers().contains(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove player from prev world
|
||||
if (player.getWorld() != null) {
|
||||
player.getWorld().removePlayer(player);
|
||||
}
|
||||
|
||||
// Register
|
||||
player.setWorld(this);
|
||||
this.getPlayers().add(player);
|
||||
|
||||
// Set player variables
|
||||
player.setPeerId(this.getNextPeerId());
|
||||
player.getTeamManager().setEntity(new EntityTeam(player));
|
||||
// player.getTeamManager().setEntityId(this.getNextEntityId(EntityIdType.TEAM));
|
||||
|
||||
// Copy main team to multiplayer team
|
||||
if (this.isMultiplayer()) {
|
||||
player
|
||||
.getTeamManager()
|
||||
.getMpTeam()
|
||||
.copyFrom(
|
||||
player.getTeamManager().getCurrentSinglePlayerTeamInfo(),
|
||||
player.getTeamManager().getMaxTeamSize());
|
||||
player.getTeamManager().setCurrentCharacterIndex(0);
|
||||
|
||||
if (player != this.getHost()) {
|
||||
this.broadcastPacket(
|
||||
new PacketPlayerChatNotify(
|
||||
player,
|
||||
0,
|
||||
SystemHint.newBuilder()
|
||||
.setType(SystemHintType.SYSTEM_HINT_TYPE_CHAT_ENTER_WORLD.getNumber())
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
// Add to scene
|
||||
player.setSceneId(newSceneId);
|
||||
Scene scene = this.getSceneById(player.getSceneId());
|
||||
scene.addPlayer(player);
|
||||
|
||||
// Info packet for other players
|
||||
if (this.getPlayers().size() > 1) {
|
||||
this.updatePlayerInfos(player);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removePlayer(Player player) {
|
||||
// Remove team entities
|
||||
player.sendPacket(
|
||||
@@ -389,7 +466,7 @@ public class World implements Iterable<Player> {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updatePlayerInfos(Player paramPlayer) {
|
||||
protected void updatePlayerInfos(Player paramPlayer) {
|
||||
for (Player player : this.getPlayers()) {
|
||||
// Dont send packets if player is logging in and filter out joining player
|
||||
if (!player.hasSentLoginPackets() || player == paramPlayer) {
|
||||
@@ -408,7 +485,7 @@ public class World implements Iterable<Player> {
|
||||
}
|
||||
|
||||
// Dont send packets if player is loading into the scene
|
||||
if (player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue()) {
|
||||
if (player.getSceneLoadState().getValue() >= SceneLoadState.INIT.getValue()) {
|
||||
// World player info packets
|
||||
player.getSession().send(new PacketWorldPlayerInfoNotify(this));
|
||||
player.getSession().send(new PacketScenePlayerInfoNotify(this));
|
||||
|
||||
Reference in New Issue
Block a user