From bee654c64fa454d2f3db6824001ac53bc88d7850 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 09:39:29 -0700 Subject: [PATCH 01/17] Implement support for multiple scenes in a world --- .../grasscutter/commands/PlayerCommands.java | 18 +- .../emu/grasscutter/game/GenshinPlayer.java | 13 +- .../emu/grasscutter/game/GenshinScene.java | 317 ++++++++++++++++++ .../emu/grasscutter/game/InvokeHandler.java | 4 +- .../emu/grasscutter/game/TeamManager.java | 14 +- src/main/java/emu/grasscutter/game/World.java | 298 +++------------- .../game/avatar/AvatarStorage.java | 2 +- .../grasscutter/game/entity/EntityAvatar.java | 11 +- .../game/entity/EntityClientGadget.java | 5 +- .../grasscutter/game/entity/EntityGadget.java | 5 +- .../grasscutter/game/entity/EntityItem.java | 7 +- .../game/entity/EntityMonster.java | 9 +- .../game/entity/GenshinEntity.java | 15 +- .../game/managers/MultiplayerManager.java | 10 +- .../grasscutter/net/packet/PacketOpcodes.java | 1 + .../packet/recv/HandlerChangeGameTimeReq.java | 4 +- .../recv/HandlerCombatInvocationsNotify.java | 4 +- .../packet/recv/HandlerEnterSceneDoneReq.java | 4 +- .../recv/HandlerEntityAiSyncNotify.java | 2 +- .../recv/HandlerEvtCreateGadgetNotify.java | 6 +- .../recv/HandlerEvtDestroyGadgetNotify.java | 2 +- .../server/packet/recv/HandlerMarkMapReq.java | 14 +- .../recv/HandlerSceneInitFinishReq.java | 6 +- .../packet/send/PacketChangeGameTimeRsp.java | 5 +- .../packet/send/PacketGetSceneAreaRsp.java | 4 +- .../send/PacketPlayerEnterSceneNotify.java | 4 - .../send/PacketPlayerGameTimeNotify.java | 4 +- .../send/PacketSceneAreaWeatherNotify.java | 4 +- .../send/PacketScenePlayerInfoNotify.java | 2 +- .../send/PacketSceneUnlockInfoNotify.java | 24 ++ 30 files changed, 487 insertions(+), 331 deletions(-) create mode 100644 src/main/java/emu/grasscutter/game/GenshinScene.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 366b5e2ee..0d2b0e9d5 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -170,12 +170,12 @@ public class PlayerCommands { float range = (5f + (.1f * count)); for (int i = 0; i < count; i++) { Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityItem entity = new EntityItem(player.getWorld(), player, itemData, pos, 1); - player.getWorld().addEntity(entity); + EntityItem entity = new EntityItem(player.getScene(), player, itemData, pos, 1); + player.getScene().addEntity(entity); } } else { - EntityItem entity = new EntityItem(player.getWorld(), player, itemData, player.getPos().clone().addY(3f), count); - player.getWorld().addEntity(entity); + EntityItem entity = new EntityItem(player.getScene(), player, itemData, player.getPos().clone().addY(3f), count); + player.getScene().addEntity(entity); } } } @@ -216,8 +216,8 @@ public class PlayerCommands { float range = (5f + (.1f * count)); for (int i = 0; i < count; i++) { Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityMonster entity = new EntityMonster(player.getWorld(), monsterData, pos, level); - player.getWorld().addEntity(entity); + EntityMonster entity = new EntityMonster(player.getScene(), monsterData, pos, level); + player.getScene().addEntity(entity); } } } @@ -227,12 +227,12 @@ public class PlayerCommands { @Override public void execute(GenshinPlayer player, String raw) { List toRemove = new LinkedList<>(); - for (GenshinEntity entity : player.getWorld().getEntities().values()) { + for (GenshinEntity entity : player.getScene().getEntities().values()) { if (entity instanceof EntityMonster) { toRemove.add(entity); } } - toRemove.forEach(e -> player.getWorld().killEntity(e, 0)); + toRemove.forEach(e -> player.getScene().killEntity(e, 0)); } } @@ -286,7 +286,7 @@ public class PlayerCommands { } entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, hp); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + entity.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); } } diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 57a561289..960018416 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -83,6 +83,7 @@ public class GenshinPlayer { @Transient private long nextGuid = 0; @Transient private int peerId; @Transient private World world; + @Transient private GenshinScene scene; @Transient private GameSession session; @Transient private AvatarStorage avatars; @Transient private Inventory inventory; @@ -206,6 +207,14 @@ public class GenshinPlayer { this.world = world; } + public GenshinScene getScene() { + return scene; + } + + public void setScene(GenshinScene scene) { + this.scene = scene; + } + public int getGmLevel() { return 1; } @@ -560,14 +569,14 @@ public class GenshinPlayer { } public void interactWith(int gadgetEntityId) { - GenshinEntity entity = getWorld().getEntityById(gadgetEntityId); + GenshinEntity entity = getScene().getEntityById(gadgetEntityId); if (entity == null) { return; } // Delete - entity.getWorld().removeEntity(entity); + entity.getScene().removeEntity(entity); // Handle if (entity instanceof EntityItem) { diff --git a/src/main/java/emu/grasscutter/game/GenshinScene.java b/src/main/java/emu/grasscutter/game/GenshinScene.java new file mode 100644 index 000000000..98fe98c71 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/GenshinScene.java @@ -0,0 +1,317 @@ +package emu.grasscutter.game; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.entity.EntityClientGadget; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.GenshinEntity; +import emu.grasscutter.game.props.ClimateType; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; +import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; +import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; +import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public class GenshinScene { + private final World world; + private final List players; + private final Int2ObjectMap entities; + private final int id; + + private int time; + private ClimateType climate; + + public GenshinScene(World world, int sceneId) { + this.world = world; + this.players = Collections.synchronizedList(new ArrayList<>()); + this.entities = new Int2ObjectOpenHashMap<>(); + this.id = sceneId; + + this.time = 8 * 60; + this.climate = ClimateType.CLIMATE_SUNNY; + } + + public int getId() { + return id; + } + + public World getWorld() { + return world; + } + + public List getPlayers() { + return players; + } + + public int getPlayerCount() { + return this.getPlayers().size(); + } + + public Int2ObjectMap getEntities() { + return entities; + } + + public GenshinEntity getEntityById(int id) { + return this.entities.get(id); + } + + public int getTime() { + return time; + } + + public void changeTime(int time) { + this.time = time % 1440; + } + + public ClimateType getClimate() { + return climate; + } + + public void setClimate(ClimateType climate) { + this.climate = climate; + } + + public boolean isInScene(GenshinEntity entity) { + return this.entities.containsKey(entity.getId()); + } + + public void addPlayer(GenshinPlayer player) { + // Check if player already in + if (getPlayers().contains(player)) { + return; + } + + // Remove player from prev scene + if (player.getScene() != null) { + player.getScene().removePlayer(player); + } + + // Add + getPlayers().add(player); + player.setSceneId(this.getId()); + player.setScene(this); + + this.setupPlayerAvatars(player); + } + + public void removePlayer(GenshinPlayer player) { + getPlayers().remove(player); + player.setScene(null); + + // Remove player avatars + this.removePlayerAvatars(player); + + // Remove player gadgets + for (EntityGadget gadget : player.getTeamManager().getGadgets()) { + this.removeEntity(gadget); + } + } + + private void setupPlayerAvatars(GenshinPlayer player) { + // Clear entities from old team + player.getTeamManager().getActiveTeam().clear(); + + // Add new entities for player + TeamInfo teamInfo = player.getTeamManager().getCurrentTeamInfo(); + for (int avatarId : teamInfo.getAvatars()) { + EntityAvatar entity = new EntityAvatar(player.getScene(), player.getAvatars().getAvatarById(avatarId)); + player.getTeamManager().getActiveTeam().add(entity); + } + + // Limit character index in case its out of bounds + if (player.getTeamManager().getCurrentCharacterIndex() >= player.getTeamManager().getActiveTeam().size() || player.getTeamManager().getCurrentCharacterIndex() < 0) { + player.getTeamManager().setCurrentCharacterIndex(player.getTeamManager().getCurrentCharacterIndex() - 1); + } + } + + private void removePlayerAvatars(GenshinPlayer player) { + Iterator it = player.getTeamManager().getActiveTeam().iterator(); + while (it.hasNext()) { + this.removeEntity(it.next(), VisionType.VisionRemove); + it.remove(); + } + } + + public void spawnPlayer(GenshinPlayer player) { + if (this.isInScene(player.getTeamManager().getCurrentAvatarEntity())) { + return; + } + + if (player.getTeamManager().getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { + player.getTeamManager().getCurrentAvatarEntity().setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f); + } + + this.addEntity(player.getTeamManager().getCurrentAvatarEntity()); + } + + private void addEntityDirectly(GenshinEntity entity) { + getEntities().put(entity.getId(), entity); + } + + public synchronized void addEntity(GenshinEntity entity) { + this.addEntityDirectly(entity); + this.broadcastPacket(new PacketSceneEntityAppearNotify(entity)); + } + + public synchronized void addEntities(Collection entities) { + for (GenshinEntity entity : entities) { + this.addEntityDirectly(entity); + } + + this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VisionBorn)); + } + + private GenshinEntity removeEntityDirectly(GenshinEntity entity) { + return getEntities().remove(entity.getId()); + } + + public void removeEntity(GenshinEntity entity) { + this.removeEntity(entity, VisionType.VisionDie); + } + + public synchronized void removeEntity(GenshinEntity entity, VisionType visionType) { + GenshinEntity removed = this.removeEntityDirectly(entity); + if (removed != null) { + this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType)); + } + } + + public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) { + this.removeEntityDirectly(oldEntity); + this.addEntityDirectly(newEntity); + this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VisionReplace)); + this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VisionReplace, oldEntity.getId())); + } + + public void showOtherEntities(GenshinPlayer player) { + List entities = new LinkedList<>(); + GenshinEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity(); + + for (GenshinEntity entity : this.getEntities().values()) { + if (entity == currentEntity) { + continue; + } + entities.add(entity); + } + + player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VisionMeet)); + } + + public void handleAttack(AttackResult result) { + //GenshinEntity attacker = getEntityById(result.getAttackerId()); + GenshinEntity target = getEntityById(result.getDefenseId()); + + if (target == null) { + return; + } + + // Godmode check + if (target instanceof EntityAvatar) { + if (((EntityAvatar) target).getPlayer().hasGodmode()) { + return; + } + } + + // Lose hp + target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage()); + + // Check if dead + boolean isDead = false; + if (target.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { + target.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); + isDead = true; + } + + // Packets + this.broadcastPacket(new PacketEntityFightPropUpdateNotify(target, FightProperty.FIGHT_PROP_CUR_HP)); + + // Check if dead + if (isDead) { + this.killEntity(target, result.getAttackerId()); + } + } + + public void killEntity(GenshinEntity target, int attackerId) { + // Packet + this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD)); + this.removeEntity(target); + + // Death event + target.onDeath(attackerId); + } + + // Gadgets + + public void onPlayerCreateGadget(EntityClientGadget gadget) { + // Directly add + this.addEntityDirectly(gadget); + + // Add to owner's gadget list + gadget.getOwner().getTeamManager().getGadgets().add(gadget); + + // Optimization + if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == gadget.getOwner()) { + return; + } + + this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityAppearNotify(gadget)); + } + + public void onPlayerDestroyGadget(int entityId) { + GenshinEntity entity = getEntities().get(entityId); + + if (entity == null || !(entity instanceof EntityClientGadget)) { + return; + } + + // Get and remove entity + EntityClientGadget gadget = (EntityClientGadget) entity; + this.removeEntityDirectly(gadget); + + // Remove from owner's gadget list + gadget.getOwner().getTeamManager().getGadgets().remove(gadget); + + // Optimization + if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == gadget.getOwner()) { + return; + } + + this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityDisappearNotify(gadget, VisionType.VisionDie)); + } + + // Broadcasting + + public void broadcastPacket(GenshinPacket packet) { + // Send to all players - might have to check if player has been sent data packets + for (GenshinPlayer player : this.getPlayers()) { + player.getSession().send(packet); + } + } + + public void broadcastPacketToOthers(GenshinPlayer excludedPlayer, GenshinPacket packet) { + // Optimization + if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == excludedPlayer) { + return; + } + // Send to all players - might have to check if player has been sent data packets + for (GenshinPlayer player : this.getPlayers()) { + if (player == excludedPlayer) { + continue; + } + // Send + player.getSession().send(packet); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/InvokeHandler.java b/src/main/java/emu/grasscutter/game/InvokeHandler.java index f94ea05f6..727f93509 100644 --- a/src/main/java/emu/grasscutter/game/InvokeHandler.java +++ b/src/main/java/emu/grasscutter/game/InvokeHandler.java @@ -46,12 +46,12 @@ public class InvokeHandler { try { if (entryListForwardAll.size() > 0) { GenshinPacket packet = packetClass.getDeclaredConstructor(List.class).newInstance(this.entryListForwardAll); - player.getWorld().broadcastPacket(packet); + player.getScene().broadcastPacket(packet); this.entryListForwardAll.clear(); } if (entryListForwardAllExceptCur.size() > 0) { GenshinPacket packet = packetClass.getDeclaredConstructor(List.class).newInstance(this.entryListForwardAllExceptCur); - player.getWorld().broadcastPacketToOthers(player, packet); + player.getScene().broadcastPacketToOthers(player, packet); this.entryListForwardAllExceptCur.clear(); } if (entryListForwardHost.size() > 0) { diff --git a/src/main/java/emu/grasscutter/game/TeamManager.java b/src/main/java/emu/grasscutter/game/TeamManager.java index cf8de4169..00f069c84 100644 --- a/src/main/java/emu/grasscutter/game/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/TeamManager.java @@ -158,7 +158,7 @@ public class TeamManager { } public boolean isSpawned() { - return getPlayer().getWorld() != null && getPlayer().getWorld().getEntities().containsKey(getCurrentAvatarEntity().getId()); + return getPlayer().getWorld() != null && getPlayer().getScene().getEntities().containsKey(getCurrentAvatarEntity().getId()); } public int getMaxTeamSize() { @@ -233,7 +233,7 @@ public class TeamManager { prevSelectedAvatarIndex = i; } } else { - entity = new EntityAvatar(getPlayer().getWorld(), getPlayer().getAvatars().getAvatarById(avatarId)); + entity = new EntityAvatar(getPlayer().getScene(), getPlayer().getAvatars().getAvatarById(avatarId)); } this.getActiveTeam().add(entity); @@ -241,7 +241,7 @@ public class TeamManager { // Unload removed entities for (EntityAvatar entity : existingAvatars.values()) { - getPlayer().getWorld().removeEntity(entity); + getPlayer().getScene().removeEntity(entity); entity.getAvatar().save(); } @@ -256,7 +256,7 @@ public class TeamManager { updateTeamResonances(); // Packets - getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer())); + getPlayer().getScene().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer())); // Run callback if (responsePacket != null) { @@ -266,7 +266,7 @@ public class TeamManager { // Check if character changed if (currentEntity != getCurrentAvatarEntity()) { // Remove and Add - getWorld().replaceEntity(currentEntity, getCurrentAvatarEntity()); + getPlayer().getScene().replaceEntity(currentEntity, getCurrentAvatarEntity()); } } @@ -396,7 +396,7 @@ public class TeamManager { oldEntity.setMotionState(MotionState.MotionStandby); // Remove and Add - getWorld().replaceEntity(oldEntity, newEntity); + getPlayer().getScene().replaceEntity(oldEntity, newEntity); getPlayer().sendPacket(new PacketChangeAvatarRsp(guid)); } @@ -426,7 +426,7 @@ public class TeamManager { } else { // Set index and spawn replacement member this.setCurrentCharacterIndex(replaceIndex); - getWorld().addEntity(replacement); + getPlayer().getScene().addEntity(replacement); } // Response packet diff --git a/src/main/java/emu/grasscutter/game/World.java b/src/main/java/emu/grasscutter/game/World.java index a06b584f1..e3eacf43e 100644 --- a/src/main/java/emu/grasscutter/game/World.java +++ b/src/main/java/emu/grasscutter/game/World.java @@ -33,24 +33,21 @@ import emu.grasscutter.server.packet.send.PacketSyncScenePlayTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketWorldPlayerInfoNotify; import emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify; +import emu.grasscutter.utils.Position; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public class World implements Iterable { private final GenshinPlayer owner; private final List players; + private final Int2ObjectMap scenes; private int levelEntityId; private int nextEntityId = 0; private int nextPeerId = 0; - private final Int2ObjectMap entities; - private int worldLevel; - private int sceneId; - private int time; - private ClimateType climate; + private boolean isMultiplayer; - private boolean isDungeon; public World(GenshinPlayer player) { this(player, false); @@ -59,11 +56,9 @@ public class World implements Iterable { public World(GenshinPlayer player, boolean isMultiplayer) { this.owner = player; this.players = Collections.synchronizedList(new ArrayList<>()); - this.entities = new Int2ObjectOpenHashMap<>(); + this.scenes = new Int2ObjectOpenHashMap<>(); + this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL); - this.sceneId = player.getSceneId(); - this.time = 8 * 60; - this.climate = ClimateType.CLIMATE_SUNNY; this.worldLevel = player.getWorldLevel(); this.isMultiplayer = isMultiplayer; } @@ -87,22 +82,6 @@ public class World implements Iterable { return ++this.nextPeerId; } - public int getSceneId() { - return sceneId; - } - - public void setSceneId(int sceneId) { - this.sceneId = sceneId; - } - - public int getTime() { - return time; - } - - public void changeTime(int time) { - this.time = time % 1440; - } - public int getWorldLevel() { return worldLevel; } @@ -111,46 +90,30 @@ public class World implements Iterable { this.worldLevel = worldLevel; } - public ClimateType getClimate() { - return climate; - } - - public void setClimate(ClimateType climate) { - this.climate = climate; - } - public List getPlayers() { return players; } + public Int2ObjectMap getScenes() { + return this.scenes; + } + + public GenshinScene getSceneById(int sceneId) { + return getScenes().computeIfAbsent(sceneId, id -> new GenshinScene(this, id)); + } + public int getPlayerCount() { return getPlayers().size(); } - public Int2ObjectMap getEntities() { - return this.entities; - } - - public boolean isInWorld(GenshinEntity entity) { - return this.entities.containsKey(entity.getId()); - } - public boolean isMultiplayer() { return isMultiplayer; } - public boolean isDungeon() { - return isDungeon; - } - public int getNextEntityId(EntityIdType idType) { return (idType.getId() << 24) + ++this.nextEntityId; } - public GenshinEntity getEntityById(int id) { - return this.entities.get(id); - } - public synchronized void addPlayer(GenshinPlayer player) { // Check if player already in if (getPlayers().contains(player)) { @@ -165,13 +128,20 @@ public class World implements Iterable { // Register player.setWorld(this); getPlayers().add(player); - + + // Set player variables player.setPeerId(this.getNextPeerId()); player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM)); - // Setup team avatars - this.setupPlayerAvatars(player); + // Copy main team to mp team + if (this.isMultiplayer()) { + player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize()); + } + // Add to scene + GenshinScene scene = this.getSceneById(player.getSceneId()); + scene.addPlayer(player); + // Info packet for other players if (this.getPlayers().size() > 1) { this.updatePlayerInfos(player); @@ -191,18 +161,15 @@ public class World implements Iterable { getPlayers().remove(player); player.setWorld(null); - this.removePlayerAvatars(player); - + // Remove from scene + GenshinScene scene = this.getSceneById(player.getSceneId()); + scene.removePlayer(player); + // Info packet for other players if (this.getPlayers().size() > 0) { this.updatePlayerInfos(player); } - - // Remove player gadgets - for (EntityGadget gadget : player.getTeamManager().getGadgets()) { - this.removeEntity(gadget); - } - + // Disband world if host leaves if (getHost() == player) { List kicked = new ArrayList<>(this.getPlayers()); @@ -210,11 +177,24 @@ public class World implements Iterable { World world = new World(victim); world.addPlayer(victim); - victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.EnterSelf, EnterReason.TeamKick, victim.getWorld().getSceneId(), victim.getPos())); + victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.EnterSelf, EnterReason.TeamKick, victim.getSceneId(), victim.getPos())); } } } + public void transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { + if (player.getScene() != null) { + player.getScene().removePlayer(player); + } + + GenshinScene scene = this.getSceneById(sceneId); + scene.addPlayer(player); + + player.getPos().set(pos); + + player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterSelf, EnterReason.TransPoint, sceneId, pos)); + } + private void updatePlayerInfos(GenshinPlayer paramPlayer) { for (GenshinPlayer player : getPlayers()) { // Dont send packets if player is loading in and filter out joining player @@ -239,185 +219,6 @@ public class World implements Iterable { } } - private void addEntityDirectly(GenshinEntity entity) { - getEntities().put(entity.getId(), entity); - } - - public synchronized void addEntity(GenshinEntity entity) { - this.addEntityDirectly(entity); - this.broadcastPacket(new PacketSceneEntityAppearNotify(entity)); - } - - public synchronized void addEntities(Collection entities) { - for (GenshinEntity entity : entities) { - this.addEntityDirectly(entity); - } - - this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VisionBorn)); - } - - private GenshinEntity removeEntityDirectly(GenshinEntity entity) { - return getEntities().remove(entity.getId()); - } - - public void removeEntity(GenshinEntity entity) { - this.removeEntity(entity, VisionType.VisionDie); - } - - public synchronized void removeEntity(GenshinEntity entity, VisionType visionType) { - GenshinEntity removed = this.removeEntityDirectly(entity); - if (removed != null) { - this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType)); - } - } - - public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) { - this.removeEntityDirectly(oldEntity); - this.addEntityDirectly(newEntity); - this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VisionReplace)); - this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VisionReplace, oldEntity.getId())); - } - - private void setupPlayerAvatars(GenshinPlayer player) { - // Copy main team to mp team - if (this.isMultiplayer()) { - player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize()); - } - - // Clear entities from old team - player.getTeamManager().getActiveTeam().clear(); - - // Add new entities for player - TeamInfo teamInfo = player.getTeamManager().getCurrentTeamInfo(); - for (int avatarId : teamInfo.getAvatars()) { - EntityAvatar entity = new EntityAvatar(this, player.getAvatars().getAvatarById(avatarId)); - player.getTeamManager().getActiveTeam().add(entity); - } - - // Limit character index in case its out of bounds - if (player.getTeamManager().getCurrentCharacterIndex() >= player.getTeamManager().getActiveTeam().size() || player.getTeamManager().getCurrentCharacterIndex() < 0) { - player.getTeamManager().setCurrentCharacterIndex(player.getTeamManager().getCurrentCharacterIndex() - 1); - } - } - - private void removePlayerAvatars(GenshinPlayer player) { - Iterator it = player.getTeamManager().getActiveTeam().iterator(); - while (it.hasNext()) { - this.removeEntity(it.next(), VisionType.VisionRemove); - it.remove(); - } - } - - public void spawnPlayer(GenshinPlayer player) { - if (isInWorld(player.getTeamManager().getCurrentAvatarEntity())) { - return; - } - - if (player.getTeamManager().getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { - player.getTeamManager().getCurrentAvatarEntity().setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f); - } - - this.addEntity(player.getTeamManager().getCurrentAvatarEntity()); - } - - public void showOtherEntities(GenshinPlayer player) { - List entities = new LinkedList<>(); - GenshinEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity(); - - for (GenshinEntity entity : this.getEntities().values()) { - if (entity == currentEntity) { - continue; - } - entities.add(entity); - } - - player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VisionMeet)); - } - - public void handleAttack(AttackResult result) { - //GenshinEntity attacker = getEntityById(result.getAttackerId()); - GenshinEntity target = getEntityById(result.getDefenseId()); - - if (target == null) { - return; - } - - // Godmode check - if (target instanceof EntityAvatar) { - if (((EntityAvatar) target).getPlayer().hasGodmode()) { - return; - } - } - - // Lose hp - target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage()); - - // Check if dead - boolean isDead = false; - if (target.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { - target.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); - isDead = true; - } - - // Packets - this.broadcastPacket(new PacketEntityFightPropUpdateNotify(target, FightProperty.FIGHT_PROP_CUR_HP)); - - // Check if dead - if (isDead) { - this.killEntity(target, result.getAttackerId()); - } - } - - public void killEntity(GenshinEntity target, int attackerId) { - // Packet - this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD)); - this.removeEntity(target); - - // Death event - target.onDeath(attackerId); - } - - // Gadgets - - public void onPlayerCreateGadget(EntityClientGadget gadget) { - // Directly add - this.addEntityDirectly(gadget); - - // Add to owner's gadget list - gadget.getOwner().getTeamManager().getGadgets().add(gadget); - - // Optimization - if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == gadget.getOwner()) { - return; - } - - this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityAppearNotify(gadget)); - } - - public void onPlayerDestroyGadget(int entityId) { - GenshinEntity entity = getEntities().get(entityId); - - if (entity == null || !(entity instanceof EntityClientGadget)) { - return; - } - - // Get and remove entity - EntityClientGadget gadget = (EntityClientGadget) entity; - this.removeEntityDirectly(gadget); - - // Remove from owner's gadget list - gadget.getOwner().getTeamManager().getGadgets().remove(gadget); - - // Optimization - if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == gadget.getOwner()) { - return; - } - - this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityDisappearNotify(gadget, VisionType.VisionDie)); - } - - // Broadcasting - public void broadcastPacket(GenshinPacket packet) { // Send to all players - might have to check if player has been sent data packets for (GenshinPlayer player : this.getPlayers()) { @@ -425,21 +226,6 @@ public class World implements Iterable { } } - public void broadcastPacketToOthers(GenshinPlayer excludedPlayer, GenshinPacket packet) { - // Optimization - if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == excludedPlayer) { - return; - } - // Send to all players - might have to check if player has been sent data packets - for (GenshinPlayer player : this.getPlayers()) { - if (player == excludedPlayer) { - continue; - } - // Send - player.getSession().send(packet); - } - } - public void close() { } diff --git a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java index a0be0b275..5162156bc 100644 --- a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java +++ b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java @@ -122,7 +122,7 @@ public class AvatarStorage implements Iterable { entity = new EntityAvatar(avatar); getPlayer().sendPacket(new PacketAvatarChangeCostumeNotify(entity)); } else { - getPlayer().getWorld().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity)); + getPlayer().getScene().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity)); } // Done diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index a46fc26ce..e82624ba7 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -5,6 +5,7 @@ import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.GenshinScene; import emu.grasscutter.game.World; import emu.grasscutter.game.avatar.GenshinAvatar; import emu.grasscutter.game.inventory.EquipType; @@ -39,14 +40,14 @@ public class EntityAvatar extends GenshinEntity { private PlayerDieType killedType; private int killedBy; - public EntityAvatar(World world, GenshinAvatar avatar) { - super(world); + public EntityAvatar(GenshinScene scene, GenshinAvatar avatar) { + super(scene); this.avatar = avatar; - this.id = world.getNextEntityId(EntityIdType.AVATAR); + this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR); GenshinItem weapon = this.getAvatar().getWeapon(); if (weapon != null) { - weapon.setWeaponEntityId(world.getNextEntityId(EntityIdType.WEAPON)); + weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON)); } } @@ -152,7 +153,7 @@ public class EntityAvatar extends GenshinEntity { .setLastMoveReliableSeq(this.getLastMoveReliableSeq()) .setLifeState(this.getLifeState().getValue()); - if (this.getWorld() != null) { + if (this.getScene() != null) { entityInfo.setMotionInfo(this.getMotionInfo()); } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java index 4abd982f2..5fbd26502 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java @@ -1,6 +1,7 @@ package emu.grasscutter.game.entity; import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.GenshinScene; import emu.grasscutter.game.World; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; @@ -34,8 +35,8 @@ public class EntityClientGadget extends EntityGadget { private int targetEntityId; private boolean asyncLoad; - public EntityClientGadget(World world, GenshinPlayer player, EvtCreateGadgetNotify notify) { - super(world); + public EntityClientGadget(GenshinScene scene, GenshinPlayer player, EvtCreateGadgetNotify notify) { + super(scene); this.owner = player; this.id = notify.getEntityId(); this.pos = new Position(notify.getInitPos()); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java index da7040b13..081c2fe3f 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java @@ -1,11 +1,12 @@ package emu.grasscutter.game.entity; +import emu.grasscutter.game.GenshinScene; import emu.grasscutter.game.World; public abstract class EntityGadget extends GenshinEntity { - public EntityGadget(World world) { - super(world); + public EntityGadget(GenshinScene scene) { + super(scene); } public abstract int getGadgetId(); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityItem.java b/src/main/java/emu/grasscutter/game/entity/EntityItem.java index b1938c75b..e96098d29 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityItem.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityItem.java @@ -2,6 +2,7 @@ package emu.grasscutter.game.entity; import emu.grasscutter.data.def.ItemData; import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.GenshinScene; import emu.grasscutter.game.World; import emu.grasscutter.game.inventory.GenshinItem; import emu.grasscutter.game.props.EntityIdType; @@ -30,9 +31,9 @@ public class EntityItem extends EntityGadget { private final GenshinItem item; private final long guid; - public EntityItem(World world, GenshinPlayer player, ItemData itemData, Position pos, int count) { - super(world); - this.id = world.getNextEntityId(EntityIdType.GADGET); + public EntityItem(GenshinScene scene, GenshinPlayer player, ItemData itemData, Position pos, int count) { + super(scene); + this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.pos = new Position(pos); this.rot = new Position(); this.guid = player.getNextGuid(); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index 9dd4810b7..93d712be0 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -4,6 +4,7 @@ import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.common.PropGrowCurve; import emu.grasscutter.data.def.MonsterCurveData; import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.game.GenshinScene; import emu.grasscutter.game.World; import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.FightProperty; @@ -36,9 +37,9 @@ public class EntityMonster extends GenshinEntity { private final int level; private int weaponEntityId; - public EntityMonster(World world, MonsterData monsterData, Position pos, int level) { - super(world); - this.id = world.getNextEntityId(EntityIdType.MONSTER); + public EntityMonster(GenshinScene scene, MonsterData monsterData, Position pos, int level) { + super(scene); + this.id = getWorld().getNextEntityId(EntityIdType.MONSTER); this.monsterData = monsterData; this.fightProp = new Int2FloatOpenHashMap(); this.pos = new Position(pos); @@ -48,7 +49,7 @@ public class EntityMonster extends GenshinEntity { // Monster weapon if (getMonsterWeaponId() > 0) { - this.weaponEntityId = world.getNextEntityId(EntityIdType.WEAPON); + this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON); } this.recalcStats(); diff --git a/src/main/java/emu/grasscutter/game/entity/GenshinEntity.java b/src/main/java/emu/grasscutter/game/entity/GenshinEntity.java index c3de3069b..c5cb9dc32 100644 --- a/src/main/java/emu/grasscutter/game/entity/GenshinEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GenshinEntity.java @@ -1,5 +1,6 @@ package emu.grasscutter.game.entity; +import emu.grasscutter.game.GenshinScene; import emu.grasscutter.game.World; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.LifeState; @@ -12,23 +13,27 @@ import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; public abstract class GenshinEntity { protected int id; - private final World world; + private final GenshinScene scene; private MotionState moveState; private int lastMoveSceneTimeMs; private int lastMoveReliableSeq; - public GenshinEntity(World world) { - this.world = world; + public GenshinEntity(GenshinScene scene) { + this.scene = scene; this.moveState = MotionState.MotionNone; } public int getId() { return this.id; } - + public World getWorld() { - return world; + return this.getScene().getWorld(); + } + + public GenshinScene getScene() { + return this.scene; } public boolean isAlive() { diff --git a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java index 980270697..7e43114b6 100644 --- a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java +++ b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java @@ -35,10 +35,12 @@ public class MultiplayerManager { return; } + /* if (target.getWorld().isDungeon()) { player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.SceneCannotEnter)); return; } + */ // Get request CoopRequest request = target.getCoopRequests().get(player.getId()); @@ -90,14 +92,14 @@ public class MultiplayerManager { world.addPlayer(player); // Rejoin packet - player.sendPacket(new PacketPlayerEnterSceneNotify(player, player, EnterType.EnterSelf, EnterReason.HostFromSingleToMp, player.getWorld().getSceneId(), player.getPos())); + player.sendPacket(new PacketPlayerEnterSceneNotify(player, player, EnterType.EnterSelf, EnterReason.HostFromSingleToMp, player.getScene().getId(), player.getPos())); } // Make requester join player.getWorld().addPlayer(requester); // Packet - requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, player, EnterType.EnterOther, EnterReason.TeamJoin, player.getWorld().getSceneId(), player.getPos())); + requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, player, EnterType.EnterOther, EnterReason.TeamJoin, player.getScene().getId(), player.getPos())); requester.getPos().set(player.getPos()); requester.getRotation().set(player.getRotation()); } @@ -120,7 +122,7 @@ public class MultiplayerManager { world.addPlayer(player); // Packet - player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterSelf, EnterReason.TeamBack, player.getWorld().getSceneId(), player.getPos())); + player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterSelf, EnterReason.TeamBack, player.getScene().getId(), player.getPos())); return true; } @@ -147,7 +149,7 @@ public class MultiplayerManager { World world = new World(victim); world.addPlayer(victim); - victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.EnterSelf, EnterReason.TeamKick, victim.getWorld().getSceneId(), victim.getPos())); + victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.EnterSelf, EnterReason.TeamKick, victim.getScene().getId(), victim.getPos())); return true; } } diff --git a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java index 1399a6924..418f16a4b 100644 --- a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java +++ b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java @@ -958,6 +958,7 @@ public class PacketOpcodes { public static final int SceneTimeNotify = 229; public static final int SceneTransToPointReq = 256; public static final int SceneTransToPointRsp = 283; + public static final int SceneUnlockInfoNotify = 3386; public static final int SceneWeatherForcastReq = 3167; public static final int SceneWeatherForcastRsp = 3023; public static final int SeaLampCoinNotify = 2028; diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerChangeGameTimeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerChangeGameTimeReq.java index 42f633c27..dbd80ccff 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerChangeGameTimeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerChangeGameTimeReq.java @@ -14,8 +14,8 @@ public class HandlerChangeGameTimeReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { ChangeGameTimeReq req = ChangeGameTimeReq.parseFrom(payload); - session.getPlayer().getWorld().changeTime(req.getGameTime()); - session.getPlayer().sendPacket(new PacketChangeGameTimeRsp(session.getPlayer().getWorld())); + session.getPlayer().getScene().changeTime(req.getGameTime()); + session.getPlayer().sendPacket(new PacketChangeGameTimeRsp(session.getPlayer())); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java index 58ae9691d..570c3e981 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java @@ -22,12 +22,12 @@ public class HandlerCombatInvocationsNotify extends PacketHandler { case CombatEvtBeingHit: // Handle damage EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData()); - session.getPlayer().getWorld().handleAttack(hitInfo.getAttackResult()); + session.getPlayer().getScene().handleAttack(hitInfo.getAttackResult()); break; case EntityMove: // Handle movement EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData()); - GenshinEntity entity = session.getPlayer().getWorld().getEntityById(moveInfo.getEntityId()); + GenshinEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId()); if (entity != null) { entity.getPosition().set(moveInfo.getMotionInfo().getPos()); entity.getRotation().set(moveInfo.getMotionInfo().getRot()); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterSceneDoneReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterSceneDoneReq.java index 7a929f132..5b56a39f4 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterSceneDoneReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterSceneDoneReq.java @@ -24,10 +24,10 @@ public class HandlerEnterSceneDoneReq extends PacketHandler { session.send(new PacketPlayerTimeNotify(session.getPlayer())); // Probably not the right place // Spawn player in world - session.getPlayer().getWorld().spawnPlayer(session.getPlayer()); + session.getPlayer().getScene().spawnPlayer(session.getPlayer()); // Spawn other entites already in world - session.getPlayer().getWorld().showOtherEntities(session.getPlayer()); + session.getPlayer().getScene().showOtherEntities(session.getPlayer()); // Locations session.send(new PacketWorldPlayerLocationNotify(session.getPlayer().getWorld())); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEntityAiSyncNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEntityAiSyncNotify.java index 23c3b3ae6..ea0e1b847 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEntityAiSyncNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEntityAiSyncNotify.java @@ -15,7 +15,7 @@ public class HandlerEntityAiSyncNotify extends PacketHandler { EntityAiSyncNotify notify = EntityAiSyncNotify.parseFrom(payload); if (notify.getLocalAvatarAlertedMonsterListCount() > 0) { - session.getPlayer().getWorld().broadcastPacket(new PacketEntityAiSyncNotify(notify)); + session.getPlayer().getScene().broadcastPacket(new PacketEntityAiSyncNotify(notify)); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java index 9fbd97fdf..9b1cdb0fb 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java @@ -20,13 +20,13 @@ public class HandlerEvtCreateGadgetNotify extends PacketHandler { } // Sanity check - dont add duplicate entities - if (session.getPlayer().getWorld().getEntityById(notify.getEntityId()) != null) { + if (session.getPlayer().getScene().getEntityById(notify.getEntityId()) != null) { return; } // Create entity and summon in world - EntityClientGadget gadget = new EntityClientGadget(session.getPlayer().getWorld(), session.getPlayer(), notify); - session.getPlayer().getWorld().onPlayerCreateGadget(gadget); + EntityClientGadget gadget = new EntityClientGadget(session.getPlayer().getScene(), session.getPlayer(), notify); + session.getPlayer().getScene().onPlayerCreateGadget(gadget); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java index d391860f6..608215d0a 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java @@ -18,7 +18,7 @@ public class HandlerEvtDestroyGadgetNotify extends PacketHandler { return; } - session.getPlayer().getWorld().onPlayerDestroyGadget(notify.getEntityId()); + session.getPlayer().getScene().onPlayerDestroyGadget(notify.getEntityId()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java index 3a651e4cb..a91da56ad 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java @@ -1,12 +1,16 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.World; +import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq; import emu.grasscutter.net.proto.OperationOuterClass.Operation; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; @Opcodes(PacketOpcodes.MarkMapReq) @@ -19,14 +23,18 @@ public class HandlerMarkMapReq extends PacketHandler { if (req.getOp() != Operation.Add) { return; } - + session.getPlayer().getPos().setX(req.getMark().getPos().getX()); session.getPlayer().getPos().setZ(req.getMark().getPos().getZ()); session.getPlayer().getPos().setY(300); - Grasscutter.getLogger().info("Player [" + session.getPlayer().getId() + ":" + session.getPlayer().getNickname() + "] tp to " + session.getPlayer().getPos()); + Grasscutter.getLogger().info("Player [" + session.getPlayer().getId() + ":" + session.getPlayer().getNickname() + "] tp to " + session.getPlayer().getPos() + " Scene id: " + req.getMark().getSceneId()); - session.getPlayer().getWorld().broadcastPacket(new PacketSceneEntityAppearNotify(session.getPlayer())); + if (req.getMark().getSceneId() != session.getPlayer().getSceneId()) { + session.getPlayer().getWorld().transferPlayerToScene(session.getPlayer(), req.getMark().getSceneId(), session.getPlayer().getPos()); + } else { + session.getPlayer().getScene().broadcastPacket(new PacketSceneEntityAppearNotify(session.getPlayer())); + } } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneInitFinishReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneInitFinishReq.java index 19a5cf955..4a4dbfcf1 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneInitFinishReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneInitFinishReq.java @@ -14,6 +14,7 @@ import emu.grasscutter.server.packet.send.PacketSceneInitFinishRsp; import emu.grasscutter.server.packet.send.PacketScenePlayerInfoNotify; import emu.grasscutter.server.packet.send.PacketSceneTeamUpdateNotify; import emu.grasscutter.server.packet.send.PacketSceneTimeNotify; +import emu.grasscutter.server.packet.send.PacketSceneUnlockInfoNotify; import emu.grasscutter.server.packet.send.PacketServerTimeNotify; import emu.grasscutter.server.packet.send.PacketSyncScenePlayTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify; @@ -29,13 +30,14 @@ public class HandlerSceneInitFinishReq extends PacketHandler { session.send(new PacketServerTimeNotify()); session.send(new PacketWorldPlayerInfoNotify(session.getPlayer().getWorld())); session.send(new PacketWorldDataNotify(session.getPlayer().getWorld())); + session.send(new PacketSceneUnlockInfoNotify()); session.send(new GenshinPacket(PacketOpcodes.SceneForceUnlockNotify)); session.send(new PacketHostPlayerNotify(session.getPlayer().getWorld())); session.send(new PacketSceneTimeNotify(session.getPlayer())); - session.send(new PacketPlayerGameTimeNotify(session.getPlayer().getWorld(), session.getPlayer())); + session.send(new PacketPlayerGameTimeNotify(session.getPlayer())); session.send(new PacketPlayerEnterSceneInfoNotify(session.getPlayer())); - session.send(new PacketSceneAreaWeatherNotify(session.getPlayer().getWorld(), session.getPlayer())); + session.send(new PacketSceneAreaWeatherNotify(session.getPlayer())); session.send(new PacketScenePlayerInfoNotify(session.getPlayer().getWorld())); session.send(new PacketSceneTeamUpdateNotify(session.getPlayer())); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketChangeGameTimeRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketChangeGameTimeRsp.java index 5a97331f8..45a485553 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketChangeGameTimeRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketChangeGameTimeRsp.java @@ -1,5 +1,6 @@ package emu.grasscutter.server.packet.send; +import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.World; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.PacketOpcodes; @@ -7,11 +8,11 @@ import emu.grasscutter.net.proto.ChangeGameTimeRspOuterClass.ChangeGameTimeRsp; public class PacketChangeGameTimeRsp extends GenshinPacket { - public PacketChangeGameTimeRsp(World world) { + public PacketChangeGameTimeRsp(GenshinPlayer player) { super(PacketOpcodes.ChangeGameTimeRsp); ChangeGameTimeRsp proto = ChangeGameTimeRsp.newBuilder() - .setCurGameTime(world.getTime()) + .setCurGameTime(player.getScene().getTime()) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetSceneAreaRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetSceneAreaRsp.java index 8a8bdca38..e0f6c5a03 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetSceneAreaRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetSceneAreaRsp.java @@ -16,8 +16,8 @@ public class PacketGetSceneAreaRsp extends GenshinPacket { this.buildHeader(0); GetSceneAreaRsp p = GetSceneAreaRsp.newBuilder() - .setSceneId(3) - .addAllAreaIdList(Arrays.stream(new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18,19}).boxed().collect(Collectors.toList())) + .setSceneId(sceneId) + .addAllAreaIdList(Arrays.stream(new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18,19,100,101,102,103,200,210,300}).boxed().collect(Collectors.toList())) .addCityInfoList(CityInfo.newBuilder().setCityId(1).setLevel(1).build()) .addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build()) .addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build()) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java index a40663b14..d7c7361a9 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java @@ -29,10 +29,6 @@ public class PacketPlayerEnterSceneNotify extends GenshinPacket { .setWorldLevel(player.getWorldLevel()) .setEnterReason(EnterReason.Login.getValue()) .setIsFirstLoginEnterScene(player.isFirstLoginEnterScene()) - .addSceneTagIdList(102) - .addSceneTagIdList(107) - .addSceneTagIdList(113) - .addSceneTagIdList(117) .setUnk1(1) .setUnk2("3-" + player.getId() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402) .build(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerGameTimeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerGameTimeNotify.java index 08502cdf3..32fa3194b 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerGameTimeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerGameTimeNotify.java @@ -8,11 +8,11 @@ import emu.grasscutter.net.proto.PlayerGameTimeNotifyOuterClass.PlayerGameTimeNo public class PacketPlayerGameTimeNotify extends GenshinPacket { - public PacketPlayerGameTimeNotify(World world, GenshinPlayer player) { + public PacketPlayerGameTimeNotify(GenshinPlayer player) { super(PacketOpcodes.PlayerGameTimeNotify); PlayerGameTimeNotify proto = PlayerGameTimeNotify.newBuilder() - .setGameTime(world.getTime()) + .setGameTime(player.getScene().getTime()) .setUid(player.getId()) .build(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneAreaWeatherNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneAreaWeatherNotify.java index 0cf622977..de26dd064 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneAreaWeatherNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneAreaWeatherNotify.java @@ -8,12 +8,12 @@ import emu.grasscutter.net.proto.SceneAreaWeatherNotifyOuterClass.SceneAreaWeath public class PacketSceneAreaWeatherNotify extends GenshinPacket { - public PacketSceneAreaWeatherNotify(World world, GenshinPlayer player) { + public PacketSceneAreaWeatherNotify(GenshinPlayer player) { super(PacketOpcodes.SceneAreaWeatherNotify); SceneAreaWeatherNotify proto = SceneAreaWeatherNotify.newBuilder() .setWeatherAreaId(1) - .setClimateType(world.getClimate().getValue()) + .setClimateType(player.getScene().getClimate().getValue()) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerInfoNotify.java index 8e6830167..a653889e2 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerInfoNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerInfoNotify.java @@ -21,7 +21,7 @@ public class PacketScenePlayerInfoNotify extends GenshinPacket { .setUid(p.getId()) .setPeerId(p.getPeerId()) .setName(p.getNickname()) - .setSceneId(world.getSceneId()) + .setSceneId(p.getSceneId()) .setOnlinePlayerInfo(p.getOnlinePlayerInfo()) .build(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java new file mode 100644 index 000000000..857f9879c --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java @@ -0,0 +1,24 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SceneUnlockInfoNotifyOuterClass.SceneUnlockInfoNotify; +import emu.grasscutter.net.proto.SceneUnlockInfoOuterClass.SceneUnlockInfo; + +public class PacketSceneUnlockInfoNotify extends GenshinPacket { + + public PacketSceneUnlockInfoNotify() { + super(PacketOpcodes.SceneUnlockInfoNotify); // Rename opcode later + + SceneUnlockInfoNotify proto = SceneUnlockInfoNotify.newBuilder() + .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(1)) + .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(3)) + .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(4)) + .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(5)) + .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(6)) + .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(7)) + .build(); + + this.setData(proto); + } +} From be89a6099e6eeac69d1d6e7a279e65f8727a75a7 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 09:59:00 -0700 Subject: [PATCH 02/17] Simple sanity check when changing scenes --- src/main/java/emu/grasscutter/game/World.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/emu/grasscutter/game/World.java b/src/main/java/emu/grasscutter/game/World.java index e3eacf43e..31c4c9f2f 100644 --- a/src/main/java/emu/grasscutter/game/World.java +++ b/src/main/java/emu/grasscutter/game/World.java @@ -183,6 +183,10 @@ public class World implements Iterable { } public void transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { + if (player.getScene().getId() == sceneId) { + return; + } + if (player.getScene() != null) { player.getScene().removePlayer(player); } From 7bac86ed41e9a046ab155547656acca2d67795d0 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 09:59:21 -0700 Subject: [PATCH 03/17] Add !changescene command --- .../emu/grasscutter/commands/PlayerCommands.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 0bd700356..31f820136 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -325,4 +325,20 @@ public class PlayerCommands { player.getInventory().removeItems(toRemove); } } + + @Command(aliases = {"scene"}, helpText = "/Changescene [Scene id]") + public static class ChangeScene extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + int sceneId = 0; + + try { + sceneId = Integer.parseInt(raw); + } catch (Exception e) { + return; + } + + player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); + } + } } From 495ca5c16c73656ec2ca846e347ee6fa6bf69a76 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 09:59:57 -0700 Subject: [PATCH 04/17] Fix small issue with command parsing --- src/main/java/emu/grasscutter/commands/PlayerCommands.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 31f820136..4e8c2e57e 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -79,7 +79,7 @@ public class PlayerCommands { return; } // Execute - int len = Math.min(first.length() + 1, msg.length()); + int len = Math.min(split[0].length() + 1, msg.length()); cmd.execute(player, msg.substring(len)); } } From d3eb0dcfc29ae0a19fe8784f31a07562998aab89 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:14:32 -0700 Subject: [PATCH 05/17] Add SceneDatas --- .../grasscutter/commands/PlayerCommands.java | 6 ++- .../emu/grasscutter/data/GenshinData.java | 6 +++ .../emu/grasscutter/data/def/SceneData.java | 32 +++++++++++++ .../emu/grasscutter/game/GenshinScene.java | 20 ++++++--- src/main/java/emu/grasscutter/game/World.java | 29 +++++++++--- .../emu/grasscutter/game/props/SceneType.java | 45 +++++++++++++++++++ 6 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/def/SceneData.java create mode 100644 src/main/java/emu/grasscutter/game/props/SceneType.java diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 4e8c2e57e..8c8433b4f 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -338,7 +338,11 @@ public class PlayerCommands { return; } - player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); + boolean result = player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); + + if (!result) { + player.dropMessage("Scene does not exist!"); + } } } } diff --git a/src/main/java/emu/grasscutter/data/GenshinData.java b/src/main/java/emu/grasscutter/data/GenshinData.java index 84614618a..bb2709108 100644 --- a/src/main/java/emu/grasscutter/data/GenshinData.java +++ b/src/main/java/emu/grasscutter/data/GenshinData.java @@ -51,6 +51,8 @@ public class GenshinData { private static final Int2ObjectMap avatarCostumeDataMap = new Int2ObjectLinkedOpenHashMap<>(); private static final Int2ObjectMap avatarCostumeDataItemIdMap = new Int2ObjectLinkedOpenHashMap<>(); + private static final Int2ObjectMap sceneDataMap = new Int2ObjectLinkedOpenHashMap<>(); + public static Int2ObjectMap getMapByResourceDef(Class resourceDefinition) { Int2ObjectMap map = null; @@ -209,4 +211,8 @@ public class GenshinData { public static Int2ObjectMap getAvatarCostumeDataItemIdMap() { return avatarCostumeDataItemIdMap; } + + public static Int2ObjectMap getSceneDataMap() { + return sceneDataMap; + } } diff --git a/src/main/java/emu/grasscutter/data/def/SceneData.java b/src/main/java/emu/grasscutter/data/def/SceneData.java new file mode 100644 index 000000000..d9c0bd0ab --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/SceneData.java @@ -0,0 +1,32 @@ +package emu.grasscutter.data.def; + +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.GenshinResource; +import emu.grasscutter.data.ResourceType; + +import emu.grasscutter.game.props.SceneType; + +@ResourceType(name = "SceneExcelConfigData.json") +public class SceneData extends GenshinResource { + private int Id; + private SceneType SceneType; + private String ScriptData; + + @Override + public int getId() { + return this.Id; + } + + public SceneType getSceneType() { + return SceneType; + } + + public String getScriptData() { + return ScriptData; + } + + @Override + public void onLoad() { + + } +} diff --git a/src/main/java/emu/grasscutter/game/GenshinScene.java b/src/main/java/emu/grasscutter/game/GenshinScene.java index 98fe98c71..36de90ea0 100644 --- a/src/main/java/emu/grasscutter/game/GenshinScene.java +++ b/src/main/java/emu/grasscutter/game/GenshinScene.java @@ -7,6 +7,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import emu.grasscutter.data.def.SceneData; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityClientGadget; import emu.grasscutter.game.entity.EntityGadget; @@ -14,6 +15,7 @@ import emu.grasscutter.game.entity.GenshinEntity; import emu.grasscutter.game.props.ClimateType; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.game.props.SceneType; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; @@ -26,30 +28,38 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public class GenshinScene { private final World world; + private final SceneData sceneData; private final List players; private final Int2ObjectMap entities; - private final int id; private int time; private ClimateType climate; - public GenshinScene(World world, int sceneId) { + public GenshinScene(World world, SceneData sceneData) { this.world = world; + this.sceneData = sceneData; this.players = Collections.synchronizedList(new ArrayList<>()); this.entities = new Int2ObjectOpenHashMap<>(); - this.id = sceneId; - + this.time = 8 * 60; this.climate = ClimateType.CLIMATE_SUNNY; } public int getId() { - return id; + return sceneData.getId(); } public World getWorld() { return world; } + + public SceneData getSceneData() { + return this.sceneData; + } + + public SceneType getSceneType() { + return getSceneData().getSceneType(); + } public List getPlayers() { return players; diff --git a/src/main/java/emu/grasscutter/game/World.java b/src/main/java/emu/grasscutter/game/World.java index 31c4c9f2f..b3fb3e150 100644 --- a/src/main/java/emu/grasscutter/game/World.java +++ b/src/main/java/emu/grasscutter/game/World.java @@ -14,6 +14,8 @@ import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.SceneData; import emu.grasscutter.game.GenshinPlayer.SceneLoadState; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityClientGadget; @@ -99,7 +101,21 @@ public class World implements Iterable { } public GenshinScene getSceneById(int sceneId) { - return getScenes().computeIfAbsent(sceneId, id -> new GenshinScene(this, id)); + // Get scene normally + GenshinScene scene = getScenes().get(sceneId); + if (scene != null) { + return scene; + } + + // Create scene from scene data if it doesnt exist + SceneData sceneData = GenshinData.getSceneDataMap().get(sceneId); + if (sceneData != null) { + scene = new GenshinScene(this, sceneData); + this.getScenes().put(sceneId, scene); + return scene; + } + + return null; } public int getPlayerCount() { @@ -182,9 +198,9 @@ public class World implements Iterable { } } - public void transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { - if (player.getScene().getId() == sceneId) { - return; + public boolean transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { + if (player.getScene().getId() == sceneId || GenshinData.getSceneDataMap().get(sceneId) == null) { + return false; } if (player.getScene() != null) { @@ -193,10 +209,11 @@ public class World implements Iterable { GenshinScene scene = this.getSceneById(sceneId); scene.addPlayer(player); - player.getPos().set(pos); - + + // Teleport packet player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterSelf, EnterReason.TransPoint, sceneId, pos)); + return true; } private void updatePlayerInfos(GenshinPlayer paramPlayer) { diff --git a/src/main/java/emu/grasscutter/game/props/SceneType.java b/src/main/java/emu/grasscutter/game/props/SceneType.java new file mode 100644 index 000000000..d6fbaf40d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/props/SceneType.java @@ -0,0 +1,45 @@ +package emu.grasscutter.game.props; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public enum SceneType { + SCENE_NONE (0), + SCENE_WORLD (1), + SCENE_DUNGEON (2), + SCENE_ROOM (3), + SCENE_HOME_WORLD (4), + SCENE_HOME_ROOM (5), + SCENE_ACTIVITY (6); + + private final int value; + private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + private static final Map stringMap = new HashMap<>(); + + static { + Stream.of(values()).forEach(e -> { + map.put(e.getValue(), e); + stringMap.put(e.name(), e); + }); + } + + private SceneType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static SceneType getTypeByValue(int value) { + return map.getOrDefault(value, SCENE_NONE); + } + + public static SceneType getTypeByName(String name) { + return stringMap.getOrDefault(name, SCENE_NONE); + } +} From c1d5b1fc8fcdd3a9a154a6ed59637c839b94f115 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:14:45 -0700 Subject: [PATCH 06/17] Set scene names to generate in the handbook --- src/main/java/emu/grasscutter/tools/Tools.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/emu/grasscutter/tools/Tools.java b/src/main/java/emu/grasscutter/tools/Tools.java index b7c565672..2e4a7e153 100644 --- a/src/main/java/emu/grasscutter/tools/Tools.java +++ b/src/main/java/emu/grasscutter/tools/Tools.java @@ -20,6 +20,7 @@ import emu.grasscutter.data.ResourceLoader; import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.data.def.SceneData; public final class Tools { @@ -63,6 +64,17 @@ public final class Tools { writer.println(); + writer.println("// Scenes"); + list = new ArrayList<>(GenshinData.getSceneDataMap().keySet()); + Collections.sort(list); + + for (Integer id : list) { + SceneData data = GenshinData.getSceneDataMap().get(id); + writer.println(data.getId() + " : " + data.getScriptData()); + } + + writer.println(); + writer.println("// Monsters"); list = new ArrayList<>(GenshinData.getMonsterDataMap().keySet()); Collections.sort(list); From e8261d568b5026c7b99e0f091b59ecfbdc018aea Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:17:19 -0700 Subject: [PATCH 07/17] Update failure message when not being able to change scene --- src/main/java/emu/grasscutter/commands/PlayerCommands.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 8c8433b4f..8be55eccc 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -341,7 +341,7 @@ public class PlayerCommands { boolean result = player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); if (!result) { - player.dropMessage("Scene does not exist!"); + player.dropMessage("Scene does not exist or you are already in it"); } } } From 057f568a3709f88a50952eceddb51cbe6b755e3c Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 21:35:01 -0700 Subject: [PATCH 08/17] Merge branch 'main' into dev-scene --- .gitignore | 3 +- run.bat | 7 + .../grasscutter/commands/PlayerCommands.java | 307 +++++++++ .../grasscutter/commands/ServerCommands.java | 171 +++++ .../java/emu/grasscutter/Grasscutter.java | 20 +- .../emu/grasscutter/commands/Command.java | 16 +- .../grasscutter/commands/CommandHandler.java | 28 + .../emu/grasscutter/commands/CommandMap.java | 159 +++++ .../grasscutter/commands/PlayerCommands.java | 647 +++++++++--------- .../grasscutter/commands/ServerCommands.java | 353 +++++----- .../emu/grasscutter/data/ResourceLoader.java | 47 +- .../grasscutter/database/DatabaseHelper.java | 1 + .../java/emu/grasscutter/game/Account.java | 19 + .../emu/grasscutter/game/GenshinPlayer.java | 18 +- .../emu/grasscutter/game/GenshinScene.java | 2 +- .../game/managers/ChatManager.java | 24 +- .../grasscutter/server/game/GameServer.java | 31 +- .../packet/recv/HandlerPlayerChatReq.java | 4 +- .../packet/recv/HandlerPrivateChatReq.java | 4 +- .../java/emu/grasscutter/utils/Utils.java | 19 + src/main/resources/logback.xml | 2 +- 21 files changed, 1334 insertions(+), 548 deletions(-) create mode 100644 run.bat create mode 100644 src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java create mode 100644 src/deprecated/java/emu/grasscutter/commands/ServerCommands.java create mode 100644 src/main/java/emu/grasscutter/commands/CommandHandler.java create mode 100644 src/main/java/emu/grasscutter/commands/CommandMap.java diff --git a/.gitignore b/.gitignore index fc8d20d93..11c43e89a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,8 @@ hs_err_pid* .gradle # Ignore Gradle build output directory -build +build/ +out/ # Eclipse .project diff --git a/run.bat b/run.bat new file mode 100644 index 000000000..36530cf77 --- /dev/null +++ b/run.bat @@ -0,0 +1,7 @@ +@echo off + + + +::This will not work if your java is in a different location, plugin as necessary +::this just saves you from changing your PATH +"C:\Program Files\Java\jdk1.8.0_202\bin\java.exe" -jar ./grasscutter.jar \ No newline at end of file diff --git a/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java b/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java new file mode 100644 index 000000000..2e8be354d --- /dev/null +++ b/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java @@ -0,0 +1,307 @@ +package emu.grasscutter.commands; + +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.avatar.GenshinAvatar; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.entity.GenshinEntity; +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.game.inventory.ItemType; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; +import emu.grasscutter.utils.Position; + +public class PlayerCommands { + private static HashMap list = new HashMap<>(); + + static { + try { + // Look for classes + for (Class cls : PlayerCommands.class.getDeclaredClasses()) { + // Get non abstract classes + if (!Modifier.isAbstract(cls.getModifiers())) { + Command commandAnnotation = cls.getAnnotation(Command.class); + PlayerCommand command = (PlayerCommand) cls.newInstance(); + + if (commandAnnotation != null) { + command.setLevel(commandAnnotation.gmLevel()); + for (String alias : commandAnnotation.aliases()) { + if (alias.length() == 0) { + continue; + } + + String commandName = "!" + alias; + list.put(commandName, command); + commandName = "/" + alias; + list.put(commandName, command); + } + } + + String commandName = "!" + cls.getSimpleName().toLowerCase(); + list.put(commandName, command); + commandName = "/" + cls.getSimpleName().toLowerCase(); + list.put(commandName, command); + } + + } + } catch (Exception e) { + + } + } + + public static void handle(GenshinPlayer player, String msg) { + String[] split = msg.split(" "); + + // End if invalid + if (split.length == 0) { + return; + } + + // + String first = split[0].toLowerCase(); + PlayerCommand c = PlayerCommands.list.get(first); + + if (c != null) { + // Level check + if (player.getGmLevel() < c.getLevel()) { + return; + } + // Execute + int len = Math.min(first.length() + 1, msg.length()); + c.execute(player, msg.substring(len)); + } + } + + public static abstract class PlayerCommand { + // GM level required to use this command + private int level; + protected int getLevel() { return this.level; } + protected void setLevel(int minLevel) { this.level = minLevel; } + + // Main + public abstract void execute(GenshinPlayer player, String raw); + } + + // ================ Commands ================ + + @Command(aliases = {"g", "item", "additem"}, helpText = "/give [item id] [count] - Gives {count} amount of {item id}") + public static class Give extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + String[] split = raw.split(" "); + int itemId = 0, count = 1; + + try { + itemId = Integer.parseInt(split[0]); + } catch (Exception e) { + itemId = 0; + } + + try { + count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1); + } catch (Exception e) { + count = 1; + } + + // Give + ItemData itemData = GenshinData.getItemDataMap().get(itemId); + GenshinItem item; + + if (itemData == null) { + player.dropMessage("Error: Item data not found"); + return; + } + + if (itemData.isEquip()) { + List items = new LinkedList<>(); + for (int i = 0; i < count; i++) { + item = new GenshinItem(itemData); + items.add(item); + } + player.getInventory().addItems(items); + player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); + } else { + item = new GenshinItem(itemData, count); + player.getInventory().addItem(item); + player.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop)); + } + } + } + + @Command(aliases = {"d"}, helpText = "/drop [item id] [count] - Drops {count} amount of {item id}") + public static class Drop extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + String[] split = raw.split(" "); + int itemId = 0, count = 1; + + try { + itemId = Integer.parseInt(split[0]); + } catch (Exception e) { + itemId = 0; + } + + try { + count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1); + } catch (Exception e) { + count = 1; + } + + // Give + ItemData itemData = GenshinData.getItemDataMap().get(itemId); + + if (itemData == null) { + player.dropMessage("Error: Item data not found"); + return; + } + + if (itemData.isEquip()) { + float range = (5f + (.1f * count)); + for (int i = 0; i < count; i++) { + Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityItem entity = new EntityItem(player.getWorld(), player, itemData, pos, 1); + player.getWorld().addEntity(entity); + } + } else { + EntityItem entity = new EntityItem(player.getWorld(), player, itemData, player.getPos().clone().addY(3f), count); + player.getWorld().addEntity(entity); + } + } + } + + @Command(helpText = "/spawn [monster id] [count] - Creates {count} amount of {item id}") + public static class Spawn extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + String[] split = raw.split(" "); + int monsterId = 0, count = 1, level = 1; + + try { + monsterId = Integer.parseInt(split[0]); + } catch (Exception e) { + monsterId = 0; + } + + try { + level = Math.max(Math.min(Integer.parseInt(split[1]), 200), 1); + } catch (Exception e) { + level = 1; + } + + try { + count = Math.max(Math.min(Integer.parseInt(split[2]), 1000), 1); + } catch (Exception e) { + count = 1; + } + + // Give + MonsterData monsterData = GenshinData.getMonsterDataMap().get(monsterId); + + if (monsterData == null) { + player.dropMessage("Error: Monster data not found"); + return; + } + + float range = (5f + (.1f * count)); + for (int i = 0; i < count; i++) { + Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityMonster entity = new EntityMonster(player.getWorld(), monsterData, pos, level); + player.getWorld().addEntity(entity); + } + } + } + + @Command(helpText = "/killall") + public static class KillAll extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + List toRemove = new LinkedList<>(); + for (GenshinEntity entity : player.getWorld().getEntities().values()) { + if (entity instanceof EntityMonster) { + toRemove.add(entity); + } + } + toRemove.forEach(e -> player.getWorld().killEntity(e, 0)); + } + } + + @Command(helpText = "/resetconst - Resets all constellations for the currently active character") + public static class ResetConst extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + + if (entity == null) { + return; + } + + GenshinAvatar avatar = entity.getAvatar(); + + avatar.getTalentIdList().clear(); + avatar.setCoreProudSkillLevel(0); + avatar.recalcStats(); + avatar.save(); + + player.dropMessage("Constellations for " + entity.getAvatar().getAvatarData().getName() + " have been reset. Please relogin to see changes."); + } + } + + @Command(helpText = "/godmode - Prevents you from taking damage") + public static class Godmode extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + player.setGodmode(!player.hasGodmode()); + player.dropMessage("Godmode is now " + (player.hasGodmode() ? "ON" : "OFF")); + } + } + + @Command(helpText = "/sethp [hp]") + public static class Sethp extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + String[] split = raw.split(" "); + int hp = 0; + + try { + hp = Math.max(Integer.parseInt(split[0]), 1); + } catch (Exception e) { + hp = 1; + } + + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + + if (entity == null) { + return; + } + + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, hp); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + } + } + + @Command(aliases = {"clearart"}, helpText = "/clearartifacts") + public static class ClearArtifacts extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, String raw) { + List toRemove = new LinkedList<>(); + for (GenshinItem item : player.getInventory().getItems().values()) { + if (item.getItemType() == ItemType.ITEM_RELIQUARY && item.getLevel() == 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) { + toRemove.add(item); + } + } + + player.getInventory().removeItems(toRemove); + } + } +} diff --git a/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java b/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java new file mode 100644 index 000000000..2039d5227 --- /dev/null +++ b/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java @@ -0,0 +1,171 @@ +package emu.grasscutter.commands; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.utils.Crypto; +import emu.grasscutter.utils.Utils; + +public class ServerCommands { + private static HashMap list = new HashMap<>(); + + static { + try { + // Look for classes + for (Class cls : ServerCommands.class.getDeclaredClasses()) { + // Get non abstract classes + if (!Modifier.isAbstract(cls.getModifiers())) { + String commandName = cls.getSimpleName().toLowerCase(); + list.put(commandName, (ServerCommand) cls.newInstance()); + } + + } + } catch (Exception e) { + + } + } + + public static void handle(String msg) { + String[] split = msg.split(" "); + + // End if invalid + if (split.length == 0) { + return; + } + + // + String first = split[0].toLowerCase(); + ServerCommand c = ServerCommands.list.get(first); + + if (c != null) { + // Execute + int len = Math.min(first.length() + 1, msg.length()); + c.execute(msg.substring(len)); + } + } + + public static abstract class ServerCommand { + public abstract void execute(String raw); + } + + // ================ Commands ================ + + public static class Reload extends ServerCommand { + @Override + public void execute(String raw) { + Grasscutter.getLogger().info("Reloading config."); + Grasscutter.loadConfig(); + Grasscutter.getDispatchServer().loadQueries(); + Grasscutter.getLogger().info("Reload complete."); + } + } + + public static class sendMsg extends ServerCommand { + @Override + public void execute(String raw) { + List split = Arrays.asList(raw.split(" ")); + + if (split.size() < 2) { + Grasscutter.getLogger().error("Invalid amount of args"); + return; + } + + String playerID = split.get(0); + String message = split.stream().skip(1).collect(Collectors.joining(" ")); + + + emu.grasscutter.game.Account account = DatabaseHelper.getAccountByPlayerId(Integer.parseInt(playerID)); + if (account != null) { + GenshinPlayer player = Grasscutter.getGameServer().getPlayerById(Integer.parseInt(playerID)); + if(player != null) { + player.dropMessage(message); + Grasscutter.getLogger().info(String.format("Successfully sent message to %s: %s", playerID, message)); + } else { + Grasscutter.getLogger().error("Player not online"); + } + } else { + Grasscutter.getLogger().error(String.format("Player %s does not exist", playerID)); + } + } + } + + public static class Account extends ServerCommand { + @Override + public void execute(String raw) { + String[] split = raw.split(" "); + + if (split.length < 2) { + Grasscutter.getLogger().error("Invalid amount of args"); + return; + } + + String command = split[0].toLowerCase(); + String username = split[1]; + + switch (command) { + case "create": + if (split.length < 2) { + Grasscutter.getLogger().error("Invalid amount of args"); + return; + } + + int reservedId = 0; + try { + reservedId = Integer.parseInt(split[2]); + } catch (Exception e) { + reservedId = 0; + } + + emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, reservedId); + if (account != null) { + Grasscutter.getLogger().info("Account created" + (reservedId > 0 ? " with an id of " + reservedId : "")); + } else { + Grasscutter.getLogger().error("Account already exists"); + } + break; + case "delete": + boolean success = DatabaseHelper.deleteAccount(username); + + if (success) { + Grasscutter.getLogger().info("Account deleted"); + } + break; + /* + case "setpw": + case "setpass": + case "setpassword": + if (split.length < 3) { + Grasscutter.getLogger().error("Invalid amount of args"); + return; + } + + account = DatabaseHelper.getAccountByName(username); + + if (account == null) { + Grasscutter.getLogger().error("No account found!"); + return; + } + + token = split[2]; + token = PasswordHelper.hashPassword(token); + + account.setPassword(token); + DatabaseHelper.saveAccount(account); + + Grasscutter.getLogger().info("Password set"); + break; + */ + } + } + } +} diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index f0fa2cc89..83afe274c 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -7,14 +7,15 @@ import java.io.FileWriter; import java.io.InputStreamReader; import java.net.InetSocketAddress; +import emu.grasscutter.commands.CommandMap; import emu.grasscutter.utils.Utils; +import org.reflections.Reflections; import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import ch.qos.logback.classic.Logger; -import emu.grasscutter.commands.ServerCommands; import emu.grasscutter.data.ResourceLoader; import emu.grasscutter.database.DatabaseManager; import emu.grasscutter.server.dispatch.DispatchServer; @@ -23,10 +24,6 @@ import emu.grasscutter.tools.Tools; import emu.grasscutter.utils.Crypto; public final class Grasscutter { - static { - System.setProperty("logback.configurationFile", "src/main/resources/logback.xml"); - } - private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class); private static Config config; @@ -37,8 +34,13 @@ public final class Grasscutter { private static DispatchServer dispatchServer; private static GameServer gameServer; + public static final Reflections reflector = new Reflections(); + static { - // Load configuration. + // Declare logback configuration. + System.setProperty("logback.configurationFile", "src/main/resources/logback.xml"); + + // Load server configuration. Grasscutter.loadConfig(); // Check server structure. Utils.startupCheck(); @@ -100,7 +102,11 @@ public final class Grasscutter { String input; try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { while ((input = br.readLine()) != null) { - ServerCommands.handle(input); + try { + CommandMap.getInstance().invoke(null, input); + } catch (Exception e) { + Grasscutter.getLogger().error("Command error: " + e.getMessage()); + } } } catch (Exception e) { Grasscutter.getLogger().error("An error occurred.", e); diff --git a/src/main/java/emu/grasscutter/commands/Command.java b/src/main/java/emu/grasscutter/commands/Command.java index aa88826dd..d1e181e20 100644 --- a/src/main/java/emu/grasscutter/commands/Command.java +++ b/src/main/java/emu/grasscutter/commands/Command.java @@ -5,9 +5,19 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Command { - String[] aliases() default ""; + String label() default ""; + + String usage() default ""; - int gmLevel() default 1; + String[] aliases() default {""}; - String helpText() default ""; + Execution execution() default Execution.ALL; + + String permission() default ""; + + enum Execution { + ALL, + CONSOLE, + PLAYER + } } diff --git a/src/main/java/emu/grasscutter/commands/CommandHandler.java b/src/main/java/emu/grasscutter/commands/CommandHandler.java new file mode 100644 index 000000000..97bd8c81f --- /dev/null +++ b/src/main/java/emu/grasscutter/commands/CommandHandler.java @@ -0,0 +1,28 @@ +package emu.grasscutter.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +public interface CommandHandler { + /* Invoked on player execution. */ + default void execute(GenshinPlayer player, List args) { } + /* Invoked on server execution. */ + default void execute(List args) { } + + /* + * Utilities. + */ + + /** + * Send a message to the target. + * @param player The player to send the message to, or null for the server console. + * @param message The message to send. + */ + static void sendMessage(GenshinPlayer player, String message) { + if(player == null) { + Grasscutter.getLogger().info(message); + } else player.dropMessage(message); + } +} diff --git a/src/main/java/emu/grasscutter/commands/CommandMap.java b/src/main/java/emu/grasscutter/commands/CommandMap.java new file mode 100644 index 000000000..e9ca260e7 --- /dev/null +++ b/src/main/java/emu/grasscutter/commands/CommandMap.java @@ -0,0 +1,159 @@ +package emu.grasscutter.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.GenshinPlayer; +import org.reflections.Reflections; + +import java.util.*; + +@SuppressWarnings("UnusedReturnValue") +public final class CommandMap { + public static CommandMap getInstance() { + return Grasscutter.getGameServer().getCommandMap(); + } + + private final Map commands = new HashMap<>(); + private final Map annotations = new HashMap<>(); + + /** + * Register a command handler. + * @param label The command label. + * @param command The command handler. + * @return Instance chaining. + */ + public CommandMap registerCommand(String label, CommandHandler command) { + Grasscutter.getLogger().debug("Registered command: " + label); + + // Get command data. + Command annotation = command.getClass().getAnnotation(Command.class); + this.annotations.put(label, annotation); + this.commands.put(label, command); + + // Register aliases. + if(annotation.aliases().length > 0) { + for (String alias : annotation.aliases()) { + this.commands.put(alias, command); + this.annotations.put(alias, annotation); + } + } return this; + } + + /** + * Removes a registered command handler. + * @param label The command label. + * @return Instance chaining. + */ + public CommandMap unregisterCommand(String label) { + Grasscutter.getLogger().debug("Unregistered command: " + label); + CommandHandler handler = this.commands.get(label); + if(handler == null) return this; + + Command annotation = handler.getClass().getAnnotation(Command.class); + this.annotations.remove(label); + this.commands.remove(label); + + // Unregister aliases. + if(annotation.aliases().length > 0) { + for (String alias : annotation.aliases()) { + this.commands.remove(alias); + this.annotations.remove(alias); + } + } + + return this; + } + + /** + * Returns a list of all registered commands. + * @return All command handlers as a list. + */ + public List getHandlers() { + return new LinkedList<>(this.commands.values()); + } + + /** + * Returns a handler by label/alias. + * @param label The command label. + * @return The command handler. + */ + public CommandHandler getHandler(String label) { + return this.commands.get(label); + } + + /** + * Invoke a command handler with the given arguments. + * @param player The player invoking the command or null for the server console. + * @param rawMessage The messaged used to invoke the command. + */ + public void invoke(GenshinPlayer player, String rawMessage) { + rawMessage = rawMessage.trim(); + if(rawMessage.length() == 0) { + CommandHandler.sendMessage(player, "No command specified."); + } + + // Remove prefix if present. + if(!Character.isLetter(rawMessage.charAt(0))) + rawMessage = rawMessage.substring(1); + + // Parse message. + String[] split = rawMessage.split(" "); + List args = new LinkedList<>(Arrays.asList(split)); + String label = args.remove(0); + + // Get command handler. + CommandHandler handler = this.commands.get(label); + if(handler == null) { + CommandHandler.sendMessage(player, "Unknown command: " + label); return; + } + + // Check for permission. + if(player != null) { + String permissionNode = this.annotations.get(label).permission(); + Account account = player.getAccount(); + List permissions = account.getPermissions(); + if(!permissions.contains("*") && !permissions.contains(permissionNode)) { + CommandHandler.sendMessage(player, "You do not have permission to run this command."); return; + } + } + + // Execution power check. + Command.Execution executionPower = this.annotations.get(label).execution(); + if(player == null && executionPower == Command.Execution.PLAYER) { + CommandHandler.sendMessage(null, "Run this command in-game."); return; + } else if (player != null && executionPower == Command.Execution.CONSOLE) { + CommandHandler.sendMessage(player, "This command can only be run from the console."); return; + } + + // Invoke execute method for handler. + if(player == null) handler.execute(args); + else handler.execute(player, args); + } + + public CommandMap() { + this(false); + } + + public CommandMap(boolean scan) { + if(scan) this.scan(); + } + + /** + * Scans for all classes annotated with {@link Command} and registers them. + */ + private void scan() { + Reflections reflector = Grasscutter.reflector; + Set> classes = reflector.getTypesAnnotatedWith(Command.class); + classes.forEach(annotated -> { + try { + Command cmdData = annotated.getAnnotation(Command.class); + Object object = annotated.newInstance(); + if (object instanceof CommandHandler) + this.registerCommand(cmdData.label(), (CommandHandler) object); + else Grasscutter.getLogger().error("Class " + annotated.getName() + " is not a CommandHandler!"); + } catch (Exception exception) { + Grasscutter.getLogger().error("Failed to register command handler for " + annotated.getSimpleName(), exception); + } + }); + } +} diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 8be55eccc..d9f094050 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -1,21 +1,18 @@ package emu.grasscutter.commands; -import java.lang.reflect.Modifier; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - +import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.GenshinScene; +import emu.grasscutter.game.World; import emu.grasscutter.game.avatar.GenshinAvatar; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.EntityMonster; -import emu.grasscutter.game.entity.GenshinEntity; import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.FightProperty; @@ -23,326 +20,342 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; import emu.grasscutter.utils.Position; -public class PlayerCommands { - private static HashMap commandList = new HashMap(); - private static HashMap commandAliasList = new HashMap(); +import java.util.LinkedList; +import java.util.List; - - static { - try { - // Look for classes - for (Class cls : PlayerCommands.class.getDeclaredClasses()) { - // Get non abstract classes - if (!Modifier.isAbstract(cls.getModifiers())) { - Command commandAnnotation = cls.getAnnotation(Command.class); - PlayerCommand command = (PlayerCommand) cls.newInstance(); - - if (commandAnnotation != null) { - command.setLevel(commandAnnotation.gmLevel()); - command.setHelpText(commandAnnotation.helpText()); - for (String alias : commandAnnotation.aliases()) { - if (alias.length() == 0) { - continue; - } +/** + * A container for player-related commands. + */ +public final class PlayerCommands { + @Command(label = "give", aliases = {"g", "item", "giveitem"}, + usage = "Usage: give [player] [amount]") + public static class GiveCommand implements CommandHandler { - String commandName = alias; - commandAliasList.put(commandName, command); - } - } + @Override + public void execute(GenshinPlayer player, List args) { + int target, item, amount = 1; - String commandName = cls.getSimpleName().toLowerCase(); - commandList.put(commandName, command); - } - - } - } catch (Exception e) { - - } - } + switch(args.size()) { + default: + CommandHandler.sendMessage(player, "Usage: give [amount]"); + return; + case 1: + try { + item = Integer.parseInt(args.get(0)); + target = player.getAccount().getPlayerId(); + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(player, "Invalid item id."); + return; + } + break; + case 2: + try { + target = Integer.parseInt(args.get(0)); + if(Grasscutter.getGameServer().getPlayerById(target) == null) { + target = player.getId(); amount = Integer.parseInt(args.get(1)); + item = Integer.parseInt(args.get(0)); + } else { + item = Integer.parseInt(args.get(1)); + } + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(player, "Invalid item or player ID."); + return; + } + break; + case 3: + try { + target = Integer.parseInt(args.get(0)); + if(Grasscutter.getGameServer().getPlayerById(target) == null) { + CommandHandler.sendMessage(player, "Invalid player ID."); return; + } - public static void handle(GenshinPlayer player, String msg) { - String[] split = msg.split(" "); - - // End if invalid - if (split.length == 0) { - return; - } + item = Integer.parseInt(args.get(1)); + amount = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(player, "Invalid item or player ID."); + return; + } + break; + } - String first = split[0].toLowerCase().substring(1); - PlayerCommand c = PlayerCommands.commandList.get(first); - PlayerCommand a = PlayerCommands.commandAliasList.get(first); - - if (c != null || a != null) { - PlayerCommand cmd = c != null ? c : a; - // Level check - if (player.getGmLevel() < cmd.getLevel()) { - return; - } - // Execute - int len = Math.min(split[0].length() + 1, msg.length()); - cmd.execute(player, msg.substring(len)); - } - } - - public static abstract class PlayerCommand { - // GM level required to use this command - private int level; - private String helpText; + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerById(target); + if(targetPlayer == null) { + CommandHandler.sendMessage(player, "Player not found."); return; + } - protected int getLevel() { return this.level; } - protected void setLevel(int minLevel) { this.level = minLevel; } + ItemData itemData = GenshinData.getItemDataMap().get(item); + if(itemData == null) { + CommandHandler.sendMessage(player, "Invalid item id."); return; + } + + this.item(targetPlayer, itemData, amount); + } - protected String getHelpText() { return this.helpText; } - protected void setHelpText(String helpText) { this.helpText = helpText; } + /** + * give [player] [itemId|itemName] [amount] + */ + @Override public void execute(List args) { + if(args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: give [amount]"); + return; + } - // Main - public abstract void execute(GenshinPlayer player, String raw); - } - - // ================ Commands ================ + try { + int target = Integer.parseInt(args.get(0)); + int item = Integer.parseInt(args.get(1)); + int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerById(target); + if(targetPlayer == null) { + CommandHandler.sendMessage(null, "Player not found."); return; + } + + ItemData itemData = GenshinData.getItemDataMap().get(item); + if(itemData == null) { + CommandHandler.sendMessage(null, "Invalid item id."); return; + } + + this.item(targetPlayer, itemData, amount); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid item or player ID."); + } + } + + private void item(GenshinPlayer player, ItemData itemData, int amount) { + GenshinItem genshinItem = new GenshinItem(itemData); + if(itemData.isEquip()) { + List items = new LinkedList<>(); + for(int i = 0; i < amount; i++) { + items.add(genshinItem); + } player.getInventory().addItems(items); + player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); + } else { + genshinItem.setCount(amount); + player.getInventory().addItem(genshinItem); + player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop)); + } + } + } + + @Command(label = "drop", aliases = {"d", "dropitem"}, + usage = "Usage: drop [amount]", + execution = Command.Execution.PLAYER) + public static class DropCommand implements CommandHandler { - @Command(aliases = {"h"}, helpText = "Shows this command") - public static class Help extends PlayerCommand { + @Override + public void execute(GenshinPlayer player, List args) { + if(args.size() < 1) { + CommandHandler.sendMessage(player, "Usage: drop [amount]"); + return; + } - @Override - public void execute(GenshinPlayer player, String raw) { - String helpMessage = "Grasscutter Commands: "; - for (Map.Entry cmd : commandList.entrySet()) { + try { + int item = Integer.parseInt(args.get(0)); + int amount = 1; if(args.size() > 1) amount = Integer.parseInt(args.get(1)); - helpMessage += "\n" + cmd.getKey() + " - " + cmd.getValue().helpText; - } + ItemData itemData = GenshinData.getItemDataMap().get(item); + if(itemData == null) { + CommandHandler.sendMessage(player, "Invalid item id."); return; + } + if (itemData.isEquip()) { + float range = (5f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityItem entity = new EntityItem(player.getScene(), player, itemData, pos, 1); + player.getScene().addEntity(entity); + } + } else { + EntityItem entity = new EntityItem(player.getScene(), player, itemData, player.getPos().clone().addY(3f), amount); + player.getScene().addEntity(entity); + } + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(player, "Invalid item or player ID."); + } + } + } + + @Command(label = "spawn", execution = Command.Execution.PLAYER, + usage = "Usage: spawn [level] [amount]") + public static class SpawnCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer player, List args) { + if(args.size() < 1) { + CommandHandler.sendMessage(null, "Usage: spawn [amount]"); + return; + } + + try { + int entity = Integer.parseInt(args.get(0)); + int level = 1; if(args.size() > 1) level = Integer.parseInt(args.get(1)); + int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); - player.dropMessage(helpMessage); - } - } - - @Command(aliases = {"g", "item", "additem"}, helpText = "/give [item id] [count] - Gives {count} amount of {item id}") - public static class Give extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int itemId = 0, count = 1; - - try { - itemId = Integer.parseInt(split[0]); - } catch (Exception e) { - itemId = 0; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1); - } catch (Exception e) { - count = 1; - } - - // Give - ItemData itemData = GenshinData.getItemDataMap().get(itemId); - GenshinItem item; - - if (itemData == null) { - player.dropMessage("Error: Item data not found"); - return; - } - - if (itemData.isEquip()) { - List items = new LinkedList<>(); - for (int i = 0; i < count; i++) { - item = new GenshinItem(itemData); - items.add(item); - } - player.getInventory().addItems(items); - player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); - } else { - item = new GenshinItem(itemData, count); - player.getInventory().addItem(item); - player.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop)); - } - } - } - - @Command(aliases = {"d"}, helpText = "/drop [item id] [count] - Drops {count} amount of {item id}") - public static class Drop extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int itemId = 0, count = 1; - - try { - itemId = Integer.parseInt(split[0]); - } catch (Exception e) { - itemId = 0; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1); - } catch (Exception e) { - count = 1; - } - - // Give - ItemData itemData = GenshinData.getItemDataMap().get(itemId); - - if (itemData == null) { - player.dropMessage("Error: Item data not found"); - return; - } - - if (itemData.isEquip()) { - float range = (5f + (.1f * count)); - for (int i = 0; i < count; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityItem entity = new EntityItem(player.getScene(), player, itemData, pos, 1); - player.getScene().addEntity(entity); - } - } else { - EntityItem entity = new EntityItem(player.getScene(), player, itemData, player.getPos().clone().addY(3f), count); - player.getScene().addEntity(entity); - } - } - } - - @Command(helpText = "/spawn [monster id] [count] - Creates {count} amount of {item id}") - public static class Spawn extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int monsterId = 0, count = 1, level = 1; - - try { - monsterId = Integer.parseInt(split[0]); - } catch (Exception e) { - monsterId = 0; - } - - try { - level = Math.max(Math.min(Integer.parseInt(split[1]), 200), 1); - } catch (Exception e) { - level = 1; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[2]), 1000), 1); - } catch (Exception e) { - count = 1; - } - - // Give - MonsterData monsterData = GenshinData.getMonsterDataMap().get(monsterId); - - if (monsterData == null) { - player.dropMessage("Error: Monster data not found"); - return; - } - - float range = (5f + (.1f * count)); - for (int i = 0; i < count; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityMonster entity = new EntityMonster(player.getScene(), monsterData, pos, level); - player.getScene().addEntity(entity); - } - } - } - - @Command(helpText = "/killall") - public static class KillAll extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - List toRemove = new LinkedList<>(); - for (GenshinEntity entity : player.getScene().getEntities().values()) { - if (entity instanceof EntityMonster) { - toRemove.add(entity); - } - } - toRemove.forEach(e -> player.getScene().killEntity(e, 0)); - } - } - - @Command(helpText = "/resetconst - Resets all constellations for the currently active character") - public static class ResetConst extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - - if (entity == null) { - return; - } - - GenshinAvatar avatar = entity.getAvatar(); - - avatar.getTalentIdList().clear(); - avatar.setCoreProudSkillLevel(0); - avatar.recalcStats(); - avatar.save(); - - player.dropMessage("Constellations for " + entity.getAvatar().getAvatarData().getName() + " have been reset. Please relogin to see changes."); - } - } - - @Command(helpText = "/godmode - Prevents you from taking damage") - public static class Godmode extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - player.setGodmode(!player.hasGodmode()); - player.dropMessage("Godmode is now " + (player.hasGodmode() ? "ON" : "OFF")); - } - } - - @Command(helpText = "/sethp [hp]") - public static class Sethp extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int hp = 0; - - try { - hp = Math.max(Integer.parseInt(split[0]), 1); - } catch (Exception e) { - hp = 1; - } - - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - - if (entity == null) { - return; - } - - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, hp); - entity.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - } - } - - @Command(aliases = {"clearart"}, helpText = "/clearartifacts") - public static class ClearArtifacts extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - List toRemove = new LinkedList<>(); - for (GenshinItem item : player.getInventory().getItems().values()) { - if (item.getItemType() == ItemType.ITEM_RELIQUARY && item.getLevel() == 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) { - toRemove.add(item); - } - } - - player.getInventory().removeItems(toRemove); - } - } - - @Command(aliases = {"scene"}, helpText = "/Changescene [Scene id]") - public static class ChangeScene extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - int sceneId = 0; - - try { - sceneId = Integer.parseInt(raw); - } catch (Exception e) { - return; - } - - boolean result = player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); - - if (!result) { - player.dropMessage("Scene does not exist or you are already in it"); - } - } - } + MonsterData entityData = GenshinData.getMonsterDataMap().get(entity); + if(entityData == null) { + CommandHandler.sendMessage(null, "Invalid entity id."); return; + } + + float range = (5f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityMonster monster = new EntityMonster(player.getScene(), entityData, pos, level); + player.getScene().addEntity(monster); + } + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid item or player ID."); + } + } + } + + @Command(label = "killall", + usage = "Usage: killall [playerUid] [sceneId]") + public static class KillAllCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer player, List args) { + GenshinScene scene = player.getScene(); + scene.getEntities().values().stream() + .filter(entity -> entity instanceof EntityMonster) + .forEach(entity -> scene.killEntity(entity, 0)); + CommandHandler.sendMessage(null, "Killing all monsters in scene " + scene.getId()); + } + + @Override + public void execute(List args) { + if(args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); return; + } + + try { + int playerUid = Integer.parseInt(args.get(0)); + int sceneId = Integer.parseInt(args.get(1)); + + GenshinPlayer player = Grasscutter.getGameServer().getPlayerById(playerUid); + if (player == null) { + CommandHandler.sendMessage(null, "Player not found or offline."); + return; + } + + GenshinScene scene = player.getWorld().getSceneById(sceneId); + if (scene == null) { + CommandHandler.sendMessage(null, "Scene not found in player world"); + return; + } + + scene.getEntities().values().stream() + .filter(entity -> entity instanceof EntityMonster) + .forEach(entity -> scene.killEntity(entity, 0)); + CommandHandler.sendMessage(null, "Killing all monsters in scene " + scene.getId()); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid arguments."); + } + } + } + + @Command(label = "resetconst", aliases = {"resetconstellation"}, + usage = "Usage: resetconst [all]", execution = Command.Execution.PLAYER) + public static class ResetConstellationCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer player, List args) { + if(args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { + player.getAvatars().forEach(this::resetConstellation); + player.dropMessage("Reset all avatars' constellations."); + } else { + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + if(entity == null) + return; + + GenshinAvatar avatar = entity.getAvatar(); + this.resetConstellation(avatar); + + player.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes."); + } + } + + private void resetConstellation(GenshinAvatar avatar) { + avatar.getTalentIdList().clear(); + avatar.setCoreProudSkillLevel(0); + avatar.recalcStats(); + avatar.save(); + } + } + + @Command(label = "godmode", + usage = "Usage: godmode", execution = Command.Execution.PLAYER) + public static class GodModeCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer player, List args) { + player.setGodmode(!player.inGodmode()); + player.dropMessage("Godmode is now " + (player.inGodmode() ? "enabled" : "disabled") + "."); + } + } + + @Command(label = "sethealth", aliases = {"sethp"}, + usage = "Usage: sethealth ", execution = Command.Execution.PLAYER) + public static class SetHealthCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer player, List args) { + if(args.size() < 1) { + CommandHandler.sendMessage(null, "Usage: sethealth "); return; + } + + try { + int health = Integer.parseInt(args.get(0)); + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + if(entity == null) + return; + + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + player.dropMessage("Health set to " + health + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid health value."); + } + } + } + + @Command(label = "clearartifacts", aliases = {"clearart"}, + usage = "Usage: clearartifacts", execution = Command.Execution.PLAYER) + public static class ClearArtifactsCommand implements CommandHandler { + @Override + public void execute(GenshinPlayer player, List args) { + Inventory playerInventory = player.getInventory(); + playerInventory.getItems().values().stream() + .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) + .filter(item -> item.getLevel() == 1 && item.getExp() == 0) + .filter(item -> !item.isLocked() && !item.isEquipped()) + .forEach(item -> playerInventory.removeItem(item, item.getCount())); + } + } + + @Command(label = "changescene", aliases = {"scene"}, + usage = "Usage: changescene ", execution = Command.Execution.PLAYER) + public static class ChangeSceneCommand implements CommandHandler { + @Override + public void execute(GenshinPlayer player, List args) { + if(args.size() < 1) { + CommandHandler.sendMessage(null, "Usage: changescene "); return; + } + + int sceneId = 0; + + try { + sceneId = Integer.parseInt(args.get(0)); + } catch (Exception e) { + return; + } + + boolean result = player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); + + if (!result) { + CommandHandler.sendMessage(null, "Scene does not exist or you are already in it"); + } + } + } } diff --git a/src/main/java/emu/grasscutter/commands/ServerCommands.java b/src/main/java/emu/grasscutter/commands/ServerCommands.java index 2039d5227..3accd3ce8 100644 --- a/src/main/java/emu/grasscutter/commands/ServerCommands.java +++ b/src/main/java/emu/grasscutter/commands/ServerCommands.java @@ -1,171 +1,212 @@ package emu.grasscutter.commands; -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.GenshinPlayer; + import java.util.List; import java.util.stream.Collectors; -import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.inventory.GenshinItem; -import emu.grasscutter.utils.Crypto; -import emu.grasscutter.utils.Utils; +/** + * A container for server-related commands. + */ +public final class ServerCommands { + @Command(label = "reload", usage = "Usage: reload") + public static class ReloadCommand implements CommandHandler { -public class ServerCommands { - private static HashMap list = new HashMap<>(); - - static { - try { - // Look for classes - for (Class cls : ServerCommands.class.getDeclaredClasses()) { - // Get non abstract classes - if (!Modifier.isAbstract(cls.getModifiers())) { - String commandName = cls.getSimpleName().toLowerCase(); - list.put(commandName, (ServerCommand) cls.newInstance()); - } - - } - } catch (Exception e) { - - } - } + @Override + public void execute(List args) { + Grasscutter.getLogger().info("Reloading config."); + Grasscutter.loadConfig(); + Grasscutter.getDispatchServer().loadQueries(); + Grasscutter.getLogger().info("Reload complete."); + } - public static void handle(String msg) { - String[] split = msg.split(" "); - - // End if invalid - if (split.length == 0) { - return; - } - - // - String first = split[0].toLowerCase(); - ServerCommand c = ServerCommands.list.get(first); - - if (c != null) { - // Execute - int len = Math.min(first.length() + 1, msg.length()); - c.execute(msg.substring(len)); - } - } - - public static abstract class ServerCommand { - public abstract void execute(String raw); - } - - // ================ Commands ================ + @Override + public void execute(GenshinPlayer player, List args) { + this.execute(args); + } + } + + @Command(label = "sendmessage", aliases = {"sendmsg", "msg"}, + usage = "Usage: sendmessage ") + public static class SendMessageCommand implements CommandHandler { - public static class Reload extends ServerCommand { - @Override - public void execute(String raw) { - Grasscutter.getLogger().info("Reloading config."); - Grasscutter.loadConfig(); - Grasscutter.getDispatchServer().loadQueries(); - Grasscutter.getLogger().info("Reload complete."); - } - } + @Override + public void execute(List args) { + if(args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: sendmessage "); return; + } + + try { + int target = Integer.parseInt(args.get(0)); + String message = String.join(" ", args.subList(1, args.size())); - public static class sendMsg extends ServerCommand { - @Override - public void execute(String raw) { - List split = Arrays.asList(raw.split(" ")); + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerById(target); + if(targetPlayer == null) { + CommandHandler.sendMessage(null, "Player not found."); return; + } + + targetPlayer.dropMessage(message); + CommandHandler.sendMessage(null, "Message sent."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid player ID."); + } + } - if (split.size() < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } + @Override + public void execute(GenshinPlayer player, List args) { + if(args.size() < 2) { + CommandHandler.sendMessage(player, "Usage: sendmessage "); return; + } - String playerID = split.get(0); - String message = split.stream().skip(1).collect(Collectors.joining(" ")); + try { + int target = Integer.parseInt(args.get(0)); + String message = String.join(" ", args.subList(1, args.size())); + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerById(target); + if(targetPlayer == null) { + CommandHandler.sendMessage(player, "Player not found."); return; + } - emu.grasscutter.game.Account account = DatabaseHelper.getAccountByPlayerId(Integer.parseInt(playerID)); - if (account != null) { - GenshinPlayer player = Grasscutter.getGameServer().getPlayerById(Integer.parseInt(playerID)); - if(player != null) { - player.dropMessage(message); - Grasscutter.getLogger().info(String.format("Successfully sent message to %s: %s", playerID, message)); - } else { - Grasscutter.getLogger().error("Player not online"); - } - } else { - Grasscutter.getLogger().error(String.format("Player %s does not exist", playerID)); - } - } - } - - public static class Account extends ServerCommand { - @Override - public void execute(String raw) { - String[] split = raw.split(" "); - - if (split.length < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - String command = split[0].toLowerCase(); - String username = split[1]; + targetPlayer.sendMessage(player, message); + CommandHandler.sendMessage(player, "Message sent."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(player, "Invalid player ID."); + } + } + } + + @Command(label = "account", + usage = "Usage: account [uid]", + execution = Command.Execution.CONSOLE) + public static class AccountCommand implements CommandHandler { - switch (command) { - case "create": - if (split.length < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - int reservedId = 0; - try { - reservedId = Integer.parseInt(split[2]); - } catch (Exception e) { - reservedId = 0; - } - - emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, reservedId); - if (account != null) { - Grasscutter.getLogger().info("Account created" + (reservedId > 0 ? " with an id of " + reservedId : "")); - } else { - Grasscutter.getLogger().error("Account already exists"); - } - break; - case "delete": - boolean success = DatabaseHelper.deleteAccount(username); - - if (success) { - Grasscutter.getLogger().info("Account deleted"); - } - break; - /* - case "setpw": - case "setpass": - case "setpassword": - if (split.length < 3) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - account = DatabaseHelper.getAccountByName(username); - - if (account == null) { - Grasscutter.getLogger().error("No account found!"); - return; - } - - token = split[2]; - token = PasswordHelper.hashPassword(token); - - account.setPassword(token); - DatabaseHelper.saveAccount(account); - - Grasscutter.getLogger().info("Password set"); - break; - */ - } - } - } + @Override + public void execute(List args) { + if(args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: account [uid]"); return; + } + + String action = args.get(0); + String username = args.get(1); + + switch(action) { + default: + CommandHandler.sendMessage(null, "Usage: account [uid]"); + return; + case "create": + int uid = 0; + if(args.size() > 2) { + try { + uid = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid UID."); return; + } + } + + Account account = DatabaseHelper.createAccountWithId(username, uid); + if(account == null) { + CommandHandler.sendMessage(null, "Account already exists."); return; + } else { + CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); + account.addPermission("*"); // Grant the player superuser permissions. + } + return; + case "delete": + if(DatabaseHelper.deleteAccount(username)) { + CommandHandler.sendMessage(null, "Account deleted."); return; + } else CommandHandler.sendMessage(null, "Account not found."); + return; + } + } + } + + @Command(label = "permission", + usage = "Usage: permission ", + execution = Command.Execution.CONSOLE) + public static class PermissionCommand implements CommandHandler { + + @Override + public void execute(List args) { + if(args.size() < 3) { + CommandHandler.sendMessage(null, "Usage: permission "); return; + } + + String action = args.get(0); + String username = args.get(1); + String permission = args.get(2); + + Account account = DatabaseHelper.getAccountByName(username); + if(account == null) { + CommandHandler.sendMessage(null, "Account not found."); return; + } + + switch(action) { + default: + CommandHandler.sendMessage(null, "Usage: permission "); + return; + case "add": + if(account.addPermission(permission)) { + CommandHandler.sendMessage(null, "Permission added."); return; + } else CommandHandler.sendMessage(null, "They already have this permission!"); + return; + case "remove": + if(account.removePermission(permission)) { + CommandHandler.sendMessage(null, "Permission removed."); return; + } else CommandHandler.sendMessage(null, "They don't have this permission!"); + return; + } + } + } + + @Command(label = "help", + usage = "Usage: help [command]") + public static class HelpCommand implements CommandHandler { + + @Override + public void execute(List args) { + List handlers = CommandMap.getInstance().getHandlers(); + List annotations = handlers.stream() + .map(handler -> handler.getClass().getAnnotation(Command.class)) + .collect(Collectors.toList()); + + if(args.size() < 1) { + StringBuilder builder = new StringBuilder("Available commands:\n"); + annotations.forEach(annotation -> builder.append(annotation.usage()).append("\n")); + CommandHandler.sendMessage(null, builder.toString()); + } else { + String command = args.get(0); + CommandHandler handler = CommandMap.getInstance().getHandler(command); + if(handler == null) { + CommandHandler.sendMessage(null, "Command not found."); return; + } + + Command annotation = handler.getClass().getAnnotation(Command.class); + CommandHandler.sendMessage(null, annotation.usage()); + } + } + + @Override + public void execute(GenshinPlayer player, List args) { + List handlers = CommandMap.getInstance().getHandlers(); + List annotations = handlers.stream() + .map(handler -> handler.getClass().getAnnotation(Command.class)) + .collect(Collectors.toList()); + + if(args.size() < 1) { + annotations.forEach(annotation -> player.dropMessage(annotation.usage())); + } else { + String command = args.get(0); + CommandHandler handler = CommandMap.getInstance().getHandler(command); + if(handler == null) { + CommandHandler.sendMessage(player, "Command not found."); return; + } + + Command annotation = handler.getClass().getAnnotation(Command.class); + CommandHandler.sendMessage(player, annotation.usage()); + } + } + } } diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index dea19e022..f5b9f4f72 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -2,20 +2,12 @@ package emu.grasscutter.data; import java.io.File; import java.io.FileReader; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; +import java.util.*; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Stream; +import emu.grasscutter.utils.Utils; import org.reflections.Reflections; import com.google.gson.reflect.TypeToken; @@ -39,19 +31,12 @@ public class ResourceLoader { } }); - classList.sort((a, b) -> { - return b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value(); - }); + classList.sort((a, b) -> b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value()); return classList; } public static void loadAll() { - // Create resource folder if it doesnt exist - File resFolder = new File(Grasscutter.getConfig().RESOURCE_FOLDER); - if (!resFolder.exists()) { - resFolder.mkdir(); - } // Load ability lists loadAbilityEmbryos(); loadOpenConfig(); @@ -110,7 +95,7 @@ public class ResourceLoader { try { loadFromResource(resourceDefinition, type, map); } catch (Exception e) { - Grasscutter.getLogger().error("Error loading resource file: " + type.name(), e); + Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e); } } } @@ -153,10 +138,16 @@ public class ResourceLoader { Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)"); embryoList = new LinkedList<>(); - File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput\\Avatar\\"); - for (File file : folder.listFiles()) { - AvatarConfig config = null; - String avatarName = null; + File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Avatar/")); + File[] files = folder.listFiles(); + if(files == null) { + Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath()); + return; + } + + for (File file : files) { + AvatarConfig config; + String avatarName; Matcher matcher = pattern.matcher(file.getName()); if (matcher.find()) { @@ -209,14 +200,18 @@ public class ResourceLoader { String[] folderNames = {"BinOutput\\Talent\\EquipTalents\\", "BinOutput\\Talent\\AvatarTalents\\"}; for (String name : folderNames) { - File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + name); + File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + name)); + File[] files = folder.listFiles(); + if(files == null) { + Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return; + } - for (File file : folder.listFiles()) { + for (File file : files) { if (!file.getName().endsWith(".json")) { continue; } - Map config = null; + Map config; try (FileReader fileReader = new FileReader(file)) { config = Grasscutter.getGsonFactory().fromJson(fileReader, type); diff --git a/src/main/java/emu/grasscutter/database/DatabaseHelper.java b/src/main/java/emu/grasscutter/database/DatabaseHelper.java index d27e47030..905c4ef64 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseHelper.java +++ b/src/main/java/emu/grasscutter/database/DatabaseHelper.java @@ -36,6 +36,7 @@ public class DatabaseHelper { if (reservedId == GenshinConstants.SERVER_CONSOLE_UID) { return null; } + exists = DatabaseHelper.getAccountByPlayerId(reservedId); if (exists != null) { return null; diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index 0bb2e9e0b..bba03916a 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -9,6 +9,8 @@ import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Utils; import dev.morphia.annotations.IndexOptions; +import java.util.List; + @Entity(value = "accounts", noClassnameStored = true) public class Account { @Id private String id; @@ -23,6 +25,7 @@ public class Account { private String token; private String sessionKey; // Session token for dispatch server + private List permissions; @Deprecated public Account() {} @@ -84,6 +87,22 @@ public class Account { this.save(); return this.sessionKey; } + + /** + * The collection of a player's permissions. + */ + public List getPermissions() { + return this.permissions; + } + + public boolean addPermission(String permission) { + if(this.permissions.contains(permission)) return false; + this.permissions.add(permission); return true; + } + + public boolean removePermission(String permission) { + return this.permissions.remove(permission); + } // TODO make unique public String generateLoginToken() { diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 960018416..4a5b585a5 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -1,11 +1,6 @@ package emu.grasscutter.game; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; +import java.util.*; import dev.morphia.annotations.*; import emu.grasscutter.GenshinConstants; @@ -494,7 +489,7 @@ public class GenshinPlayer { this.regionId = regionId; } - public boolean hasGodmode() { + public boolean inGodmode() { return godmode; } @@ -567,6 +562,15 @@ public class GenshinPlayer { public void dropMessage(Object message) { this.sendPacket(new PacketPrivateChatNotify(GenshinConstants.SERVER_CONSOLE_UID, getId(), message.toString())); } + + /** + * Sends a message to another player. + * @param sender The sender of the message. + * @param message The message to send. + */ + public void sendMessage(GenshinPlayer sender, Object message) { + this.sendPacket(new PacketPrivateChatNotify(sender.getId(), this.getId(), message.toString())); + } public void interactWith(int gadgetEntityId) { GenshinEntity entity = getScene().getEntityById(gadgetEntityId); diff --git a/src/main/java/emu/grasscutter/game/GenshinScene.java b/src/main/java/emu/grasscutter/game/GenshinScene.java index 36de90ea0..fc25546b9 100644 --- a/src/main/java/emu/grasscutter/game/GenshinScene.java +++ b/src/main/java/emu/grasscutter/game/GenshinScene.java @@ -229,7 +229,7 @@ public class GenshinScene { // Godmode check if (target instanceof EntityAvatar) { - if (((EntityAvatar) target).getPlayer().hasGodmode()) { + if (((EntityAvatar) target).getPlayer().inGodmode()) { return; } } diff --git a/src/main/java/emu/grasscutter/game/managers/ChatManager.java b/src/main/java/emu/grasscutter/game/managers/ChatManager.java index d2d1acac4..3856c0fd1 100644 --- a/src/main/java/emu/grasscutter/game/managers/ChatManager.java +++ b/src/main/java/emu/grasscutter/game/managers/ChatManager.java @@ -1,14 +1,18 @@ package emu.grasscutter.game.managers; -import emu.grasscutter.Grasscutter; -import emu.grasscutter.commands.PlayerCommands; +import emu.grasscutter.commands.CommandMap; import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketPlayerChatNotify; import emu.grasscutter.server.packet.send.PacketPrivateChatNotify; +import java.util.Arrays; +import java.util.List; + public class ChatManager { + static final List PREFIXES = Arrays.asList('/', '!'); + private final GameServer server; public ChatManager(GameServer server) { @@ -19,15 +23,15 @@ public class ChatManager { return server; } - public void sendPrivChat(GenshinPlayer player, int targetUid, String message) { + public void sendPrivateMessage(GenshinPlayer player, int targetUid, String message) { // Sanity checks if (message == null || message.length() == 0) { return; } // Check if command - if (message.charAt(0) == '!' || message.charAt(0) == '/') { - PlayerCommands.handle(player, message); + if (PREFIXES.contains(message.charAt(0))) { + CommandMap.getInstance().invoke(player, message); return; } @@ -45,7 +49,7 @@ public class ChatManager { target.sendPacket(packet); } - public void sendPrivChat(GenshinPlayer player, int targetUid, int emote) { + public void sendPrivateMessage(GenshinPlayer player, int targetUid, int emote) { // Get target GenshinPlayer target = getServer().getPlayerById(targetUid); @@ -60,15 +64,15 @@ public class ChatManager { target.sendPacket(packet); } - public void sendTeamChat(GenshinPlayer player, int channel, String message) { + public void sendTeamMessage(GenshinPlayer player, int channel, String message) { // Sanity checks if (message == null || message.length() == 0) { return; } // Check if command - if (message.charAt(0) == '!') { - PlayerCommands.handle(player, message); + if (PREFIXES.contains(message.charAt(0))) { + CommandMap.getInstance().invoke(player, message); return; } @@ -76,7 +80,7 @@ public class ChatManager { player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message)); } - public void sendTeamChat(GenshinPlayer player, int channel, int icon) { + public void sendTeamMessage(GenshinPlayer player, int channel, int icon) { // Create and send chat packet player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon)); } diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 1baf3253d..87a76d400 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -1,16 +1,12 @@ package emu.grasscutter.server.game; import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; +import emu.grasscutter.commands.CommandMap; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.dungeons.DungeonManager; @@ -23,11 +19,10 @@ import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.netty.MihoyoKcpServer; -public class GameServer extends MihoyoKcpServer { +public final class GameServer extends MihoyoKcpServer { private final InetSocketAddress address; private final GameServerPacketHandler packetHandler; - private final Timer gameLoop; - + private final Map players; private final ChatManager chatManager; @@ -36,9 +31,11 @@ public class GameServer extends MihoyoKcpServer { private final ShopManager shopManager; private final MultiplayerManager multiplayerManager; private final DungeonManager dungeonManager; + private final CommandMap commandMap; public GameServer(InetSocketAddress address) { super(address); + this.setServerInitializer(new GameServerInitializer(this)); this.address = address; this.packetHandler = new GameServerPacketHandler(PacketHandler.class); @@ -50,22 +47,22 @@ public class GameServer extends MihoyoKcpServer { this.shopManager = new ShopManager(this); this.multiplayerManager = new MultiplayerManager(this); this.dungeonManager = new DungeonManager(this); + this.commandMap = new CommandMap(true); - // Ticker - this.gameLoop = new Timer(); - this.gameLoop.scheduleAtFixedRate(new TimerTask() { + // Schedule game loop. + Timer gameLoop = new Timer(); + gameLoop.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { onTick(); } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Grasscutter.getLogger().error("An error occurred during game update.", e); } } }, new Date(), 1000L); - // Shutdown hook + // Hook into shutdown event. Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown)); } @@ -101,6 +98,10 @@ public class GameServer extends MihoyoKcpServer { return dungeonManager; } + public CommandMap getCommandMap() { + return this.commandMap; + } + public void registerPlayer(GenshinPlayer player) { getPlayers().put(player.getId(), player); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerChatReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerChatReq.java index 9a807e991..db0704ed8 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerChatReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerChatReq.java @@ -17,9 +17,9 @@ public class HandlerPlayerChatReq extends PacketHandler { ChatInfo.ContentCase content = req.getChatInfo().getContentCase(); if (content == ChatInfo.ContentCase.TEXT) { - session.getServer().getChatManager().sendTeamChat(session.getPlayer(), req.getChannelId(), req.getChatInfo().getText()); + session.getServer().getChatManager().sendTeamMessage(session.getPlayer(), req.getChannelId(), req.getChatInfo().getText()); } else if (content == ChatInfo.ContentCase.ICON) { - session.getServer().getChatManager().sendTeamChat(session.getPlayer(), req.getChannelId(), req.getChatInfo().getIcon()); + session.getServer().getChatManager().sendTeamMessage(session.getPlayer(), req.getChannelId(), req.getChatInfo().getIcon()); } session.send(new PacketPlayerChatRsp()); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPrivateChatReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPrivateChatReq.java index 081c0883a..b576bbb20 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPrivateChatReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPrivateChatReq.java @@ -15,9 +15,9 @@ public class HandlerPrivateChatReq extends PacketHandler { PrivateChatReq.ContentCase content = req.getContentCase(); if (content == PrivateChatReq.ContentCase.TEXT) { - session.getServer().getChatManager().sendPrivChat(session.getPlayer(), req.getTargetUid(), req.getText()); + session.getServer().getChatManager().sendPrivateMessage(session.getPlayer(), req.getTargetUid(), req.getText()); } else if (content == PrivateChatReq.ContentCase.ICON) { - session.getServer().getChatManager().sendPrivChat(session.getPlayer(), req.getTargetUid(), req.getIcon()); + session.getServer().getChatManager().sendPrivateMessage(session.getPlayer(), req.getTargetUid(), req.getIcon()); } //session.send(new GenshinPacket(PacketOpcodes.PrivateChatRsp)); // Unused by server diff --git a/src/main/java/emu/grasscutter/utils/Utils.java b/src/main/java/emu/grasscutter/utils/Utils.java index 4293193d1..7c5ab18ba 100644 --- a/src/main/java/emu/grasscutter/utils/Utils.java +++ b/src/main/java/emu/grasscutter/utils/Utils.java @@ -79,6 +79,15 @@ public final class Utils { return v7; } + /** + * Creates a string with the path to a file. + * @param path The path to the file. + * @return A path using the operating system's file separator. + */ + public static String toFilePath(String path) { + return path.replace("/", File.separator); + } + /** * Checks if a file exists on the file system. * @param path The path to the file. @@ -118,6 +127,16 @@ public final class Utils { } } + /** + * Get object with null fallback. + * @param nonNull The object to return if not null. + * @param fallback The object to return if null. + * @return One of the two provided objects. + */ + public static T requireNonNullElseGet(T nonNull, T fallback) { + return nonNull != null ? nonNull : fallback; + } + /** * Checks for required files and folders before startup. */ diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 66d630192..666b53b42 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,7 +1,7 @@ - [%d{HH:mm:ss}] [%level] %msg%n + [%d{HH:mm:ss}] [%highlight(%level)] %msg%n From c65443f422104528b2697d5d77237f98f74e515b Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 21:38:19 -0700 Subject: [PATCH 09/17] Fix permissions not saving --- .../emu/grasscutter/commands/ServerCommands.java | 14 ++++++++------ src/main/java/emu/grasscutter/game/Account.java | 5 ++++- .../emu/grasscutter/server/game/GameServer.java | 9 +++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/java/emu/grasscutter/commands/ServerCommands.java b/src/main/java/emu/grasscutter/commands/ServerCommands.java index 3accd3ce8..1262fae97 100644 --- a/src/main/java/emu/grasscutter/commands/ServerCommands.java +++ b/src/main/java/emu/grasscutter/commands/ServerCommands.java @@ -138,7 +138,7 @@ public final class ServerCommands { String username = args.get(1); String permission = args.get(2); - Account account = DatabaseHelper.getAccountByName(username); + Account account = Grasscutter.getGameServer().getAccountByName(username); if(account == null) { CommandHandler.sendMessage(null, "Account not found."); return; } @@ -146,18 +146,20 @@ public final class ServerCommands { switch(action) { default: CommandHandler.sendMessage(null, "Usage: permission "); - return; + break; case "add": if(account.addPermission(permission)) { - CommandHandler.sendMessage(null, "Permission added."); return; + CommandHandler.sendMessage(null, "Permission added."); } else CommandHandler.sendMessage(null, "They already have this permission!"); - return; + break; case "remove": if(account.removePermission(permission)) { - CommandHandler.sendMessage(null, "Permission removed."); return; + CommandHandler.sendMessage(null, "Permission removed."); } else CommandHandler.sendMessage(null, "They don't have this permission!"); - return; + break; } + + account.save(); } } diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index bba03916a..0d206dda1 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -9,6 +9,7 @@ import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Utils; import dev.morphia.annotations.IndexOptions; +import java.util.ArrayList; import java.util.List; @Entity(value = "accounts", noClassnameStored = true) @@ -28,7 +29,9 @@ public class Account { private List permissions; @Deprecated - public Account() {} + public Account() { + this.permissions = new ArrayList<>(); + } public String getId() { return id; diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 87a76d400..329a28e61 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -8,6 +8,7 @@ import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; import emu.grasscutter.commands.CommandMap; import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.dungeons.DungeonManager; import emu.grasscutter.game.gacha.GachaManager; @@ -138,6 +139,14 @@ public final class GameServer extends MihoyoKcpServer { return player.getSocialDetail(); } + public Account getAccountByName(String username) { + Optional playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst(); + if (playerOpt.get() != null) { + return playerOpt.get().getAccount(); + } + return DatabaseHelper.getAccountByName(username); + } + public void onTick() throws Exception { for (GenshinPlayer player : this.getPlayers().values()) { player.onTick(); From dd8d1752ed90985a4c09d1a274172f94393ab2b8 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 23:00:12 -0700 Subject: [PATCH 10/17] Deregister empty scenes if a player leaves them --- src/main/java/emu/grasscutter/game/GenshinScene.java | 6 ++++++ src/main/java/emu/grasscutter/game/World.java | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/game/GenshinScene.java b/src/main/java/emu/grasscutter/game/GenshinScene.java index fc25546b9..668902d87 100644 --- a/src/main/java/emu/grasscutter/game/GenshinScene.java +++ b/src/main/java/emu/grasscutter/game/GenshinScene.java @@ -117,6 +117,7 @@ public class GenshinScene { } public void removePlayer(GenshinPlayer player) { + // Remove player from scene getPlayers().remove(player); player.setScene(null); @@ -127,6 +128,11 @@ public class GenshinScene { for (EntityGadget gadget : player.getTeamManager().getGadgets()) { this.removeEntity(gadget); } + + // Deregister scene if not in use + if (this.getEntities().size() <= 0) { + this.getWorld().deregisterScene(this); + } } private void setupPlayerAvatars(GenshinPlayer player) { diff --git a/src/main/java/emu/grasscutter/game/World.java b/src/main/java/emu/grasscutter/game/World.java index b3fb3e150..802ab6ba0 100644 --- a/src/main/java/emu/grasscutter/game/World.java +++ b/src/main/java/emu/grasscutter/game/World.java @@ -111,7 +111,7 @@ public class World implements Iterable { SceneData sceneData = GenshinData.getSceneDataMap().get(sceneId); if (sceneData != null) { scene = new GenshinScene(this, sceneData); - this.getScenes().put(sceneId, scene); + this.registerScene(scene); return scene; } @@ -198,6 +198,14 @@ public class World implements Iterable { } } + public void registerScene(GenshinScene scene) { + this.getScenes().put(scene.getId(), scene); + } + + public void deregisterScene(GenshinScene scene) { + this.getScenes().remove(scene.getId()); + } + public boolean transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { if (player.getScene().getId() == sceneId || GenshinData.getSceneDataMap().get(sceneId) == null) { return false; From 3ea9e1574525e9ed88b4daf2488377a50a90d55e Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 23:09:48 -0700 Subject: [PATCH 11/17] Make co-op players spawn into the host's scene too --- .../game/managers/MultiplayerManager.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java index 7e43114b6..ecc41f02d 100644 --- a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java +++ b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java @@ -58,25 +58,25 @@ public class MultiplayerManager { target.sendPacket(new PacketPlayerApplyEnterMpNotify(player)); } - public void applyEnterMpReply(GenshinPlayer player, int applyUid, boolean isAgreed) { + public void applyEnterMpReply(GenshinPlayer hostPlayer, int applyUid, boolean isAgreed) { // Checks - CoopRequest request = player.getCoopRequests().get(applyUid); + CoopRequest request = hostPlayer.getCoopRequests().get(applyUid); if (request == null || request.isExpired()) { return; } // Remove now that we are handling it GenshinPlayer requester = request.getRequester(); - player.getCoopRequests().remove(applyUid); + hostPlayer.getCoopRequests().remove(applyUid); - // Sanity checks - Dont let player join if already in multiplayer + // Sanity checks - Dont let the requesting player join if they are already in multiplayer if (requester.getWorld().isMultiplayer()) { - request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(player, false, PlayerApplyEnterMpReason.PlayerCannotEnterMp)); + request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, false, PlayerApplyEnterMpReason.PlayerCannotEnterMp)); return; } // Response packet - request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(player, isAgreed, PlayerApplyEnterMpReason.PlayerJudge)); + request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, isAgreed, PlayerApplyEnterMpReason.PlayerJudge)); // Declined if (!isAgreed) { @@ -84,24 +84,27 @@ public class MultiplayerManager { } // Success - if (!player.getWorld().isMultiplayer()) { + if (!hostPlayer.getWorld().isMultiplayer()) { // Player not in multiplayer, create multiplayer world - World world = new World(player, true); + World world = new World(hostPlayer, true); // Add - world.addPlayer(player); + world.addPlayer(hostPlayer); // Rejoin packet - player.sendPacket(new PacketPlayerEnterSceneNotify(player, player, EnterType.EnterSelf, EnterReason.HostFromSingleToMp, player.getScene().getId(), player.getPos())); + hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.EnterSelf, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPos())); } // Make requester join - player.getWorld().addPlayer(requester); + hostPlayer.getWorld().addPlayer(requester); + + // Set scene pos and id of requester to the host player's + requester.getPos().set(hostPlayer.getPos()); + requester.getRotation().set(hostPlayer.getRotation()); + requester.setSceneId(hostPlayer.getSceneId()); // Packet - requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, player, EnterType.EnterOther, EnterReason.TeamJoin, player.getScene().getId(), player.getPos())); - requester.getPos().set(player.getPos()); - requester.getRotation().set(player.getRotation()); + requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.EnterOther, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPos())); } public boolean leaveCoop(GenshinPlayer player) { From 3cc025efe018133641b6089862e1ed5e18f31a88 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Mon, 18 Apr 2022 22:00:01 -0700 Subject: [PATCH 12/17] Grant superuser permissions to accounts created before the permissions update --- src/main/java/emu/grasscutter/game/Account.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index 0d206dda1..dffbb0d86 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -4,6 +4,7 @@ import dev.morphia.annotations.Collation; import dev.morphia.annotations.Entity; import dev.morphia.annotations.Id; import dev.morphia.annotations.Indexed; +import dev.morphia.annotations.PreLoad; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Utils; @@ -12,6 +13,8 @@ import dev.morphia.annotations.IndexOptions; import java.util.ArrayList; import java.util.List; +import com.mongodb.DBObject; + @Entity(value = "accounts", noClassnameStored = true) public class Account { @Id private String id; @@ -114,6 +117,14 @@ public class Account { return this.token; } + @PreLoad + public void onLoad(DBObject dbObj) { + // Grant the superuser permissions to accounts created before the permissions update + if (!dbObj.containsField("permissions")) { + this.addPermission("*"); + } + } + public void save() { DatabaseHelper.saveAccount(this); } From 7417a1b62cd149ddf2c228bf449cb3e69f8f506a Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Tue, 19 Apr 2022 01:43:38 -0700 Subject: [PATCH 13/17] Fix issue with a player not being able to spawn when they join somone in co-op with a singleplayer team of more than the max allowed amount --- src/main/java/emu/grasscutter/game/TeamManager.java | 4 ++-- src/main/java/emu/grasscutter/game/World.java | 1 + .../emu/grasscutter/game/managers/MultiplayerManager.java | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/TeamManager.java b/src/main/java/emu/grasscutter/game/TeamManager.java index 00f069c84..3f6c4ebe5 100644 --- a/src/main/java/emu/grasscutter/game/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/TeamManager.java @@ -158,7 +158,7 @@ public class TeamManager { } public boolean isSpawned() { - return getPlayer().getWorld() != null && getPlayer().getScene().getEntities().containsKey(getCurrentAvatarEntity().getId()); + return getPlayer().getScene() != null && getPlayer().getScene().getEntities().containsKey(getCurrentAvatarEntity().getId()); } public int getMaxTeamSize() { @@ -256,7 +256,7 @@ public class TeamManager { updateTeamResonances(); // Packets - getPlayer().getScene().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer())); + getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer())); // Run callback if (responsePacket != null) { diff --git a/src/main/java/emu/grasscutter/game/World.java b/src/main/java/emu/grasscutter/game/World.java index 802ab6ba0..02c0df63a 100644 --- a/src/main/java/emu/grasscutter/game/World.java +++ b/src/main/java/emu/grasscutter/game/World.java @@ -152,6 +152,7 @@ public class World implements Iterable { // Copy main team to mp team if (this.isMultiplayer()) { player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize()); + player.getTeamManager().setCurrentCharacterIndex(0); } // Add to scene diff --git a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java index ecc41f02d..0e0562421 100644 --- a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java +++ b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java @@ -95,14 +95,14 @@ public class MultiplayerManager { hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.EnterSelf, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPos())); } - // Make requester join - hostPlayer.getWorld().addPlayer(requester); - // Set scene pos and id of requester to the host player's requester.getPos().set(hostPlayer.getPos()); requester.getRotation().set(hostPlayer.getRotation()); requester.setSceneId(hostPlayer.getSceneId()); + // Make requester join + hostPlayer.getWorld().addPlayer(requester); + // Packet requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.EnterOther, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPos())); } From d9fc15955083e438da4deef6fe8913ea81875a77 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Tue, 19 Apr 2022 02:22:21 -0700 Subject: [PATCH 14/17] Refactor some commands and move inventory/team limits to the config --- src/main/java/emu/grasscutter/Config.java | 7 ++++++ .../emu/grasscutter/GenshinConstants.java | 8 ------- .../java/emu/grasscutter/Grasscutter.java | 1 + .../grasscutter/commands/PlayerCommands.java | 12 +++++----- .../grasscutter/commands/ServerCommands.java | 4 ++-- .../grasscutter/database/DatabaseHelper.java | 12 +++++----- .../emu/grasscutter/game/GenshinPlayer.java | 22 +++++++++---------- .../java/emu/grasscutter/game/TeamInfo.java | 7 +++--- .../emu/grasscutter/game/TeamManager.java | 8 ++++--- .../game/avatar/GenshinAvatar.java | 4 ++-- .../grasscutter/game/entity/EntityAvatar.java | 2 +- .../grasscutter/game/entity/EntityItem.java | 2 +- .../grasscutter/game/friends/FriendsList.java | 18 +++++++-------- .../grasscutter/game/friends/Friendship.java | 8 +++---- .../game/friends/PlayerProfile.java | 2 +- .../game/inventory/GenshinItem.java | 4 ++-- .../grasscutter/game/inventory/Inventory.java | 9 ++++---- .../game/managers/ChatManager.java | 8 +++---- .../game/managers/MultiplayerManager.java | 8 +++---- .../grasscutter/server/game/GameServer.java | 18 +++++++++------ .../grasscutter/server/game/GameSession.java | 2 +- .../recv/HandlerGetPlayerSocialDetailReq.java | 2 +- .../server/packet/recv/HandlerMarkMapReq.java | 2 +- .../recv/HandlerSetPlayerBornDataReq.java | 2 +- .../send/PacketGetPlayerAskFriendListRsp.java | 2 +- .../send/PacketGetPlayerFriendListRsp.java | 2 +- .../packet/send/PacketHostPlayerNotify.java | 2 +- .../PacketPlayerApplyEnterMpResultNotify.java | 2 +- .../packet/send/PacketPlayerChatNotify.java | 6 ++--- .../send/PacketPlayerEnterSceneNotify.java | 8 +++---- .../send/PacketPlayerGameTimeNotify.java | 2 +- .../packet/send/PacketPlayerStoreNotify.java | 3 ++- .../packet/send/PacketPullRecentChatRsp.java | 4 ++-- .../send/PacketScenePlayerInfoNotify.java | 2 +- .../send/PacketSceneTeamUpdateNotify.java | 2 +- .../send/PacketStoreWeightLimitNotify.java | 12 +++++----- .../send/PacketWorldPlayerInfoNotify.java | 2 +- .../send/PacketWorldPlayerRTTNotify.java | 2 +- .../java/emu/grasscutter/utils/Utils.java | 2 -- 39 files changed, 116 insertions(+), 109 deletions(-) diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index b7a971122..42dbb4a00 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -40,6 +40,13 @@ public final class Config { } public static class ServerOptions { + public int InventoryLimitWeapon = 2000; + public int InventoryLimitRelic = 2000; + public int InventoryLimitMaterial = 2000; + public int InventoryLimitFurniture = 2000; + public int InventoryLimitAll = 30000; + public int MaxAvatarsInTeam = 4; + public int MaxAvatarsInTeamMultiplayer = 4; public int MaxEntityLimit = 1000; // Max entity limit per world. // TODO: Enforce later. public int[] WelcomeEmotes = {2007, 1002, 4010}; public String WelcomeMotd = "Welcome to Grasscutter emu"; diff --git a/src/main/java/emu/grasscutter/GenshinConstants.java b/src/main/java/emu/grasscutter/GenshinConstants.java index bb59953e2..0f9eaa0b0 100644 --- a/src/main/java/emu/grasscutter/GenshinConstants.java +++ b/src/main/java/emu/grasscutter/GenshinConstants.java @@ -9,14 +9,6 @@ public final class GenshinConstants { public static String VERSION = "2.6.0"; public static final int MAX_TEAMS = 4; - public static final int MAX_AVATARS_IN_TEAM = 4; - - public static final int LIMIT_WEAPON = 2000; - public static final int LIMIT_RELIC = 2000; - public static final int LIMIT_MATERIAL = 2000; - public static final int LIMIT_FURNITURE = 2000; - public static final int LIMIT_ALL = 30000; - public static final int MAIN_CHARACTER_MALE = 10000005; public static final int MAIN_CHARACTER_FEMALE = 10000007; public static final Position START_POSITION = new Position(2747, 194, -1719); diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 83afe274c..c9ccb551c 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -42,6 +42,7 @@ public final class Grasscutter { // Load server configuration. Grasscutter.loadConfig(); + // Check server structure. Utils.startupCheck(); } diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index d9f094050..cc671c006 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -52,8 +52,8 @@ public final class PlayerCommands { case 2: try { target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerById(target) == null) { - target = player.getId(); amount = Integer.parseInt(args.get(1)); + if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { + target = player.getUid(); amount = Integer.parseInt(args.get(1)); item = Integer.parseInt(args.get(0)); } else { item = Integer.parseInt(args.get(1)); @@ -67,7 +67,7 @@ public final class PlayerCommands { case 3: try { target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerById(target) == null) { + if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { CommandHandler.sendMessage(player, "Invalid player ID."); return; } @@ -81,7 +81,7 @@ public final class PlayerCommands { break; } - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerById(target); + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); if(targetPlayer == null) { CommandHandler.sendMessage(player, "Player not found."); return; } @@ -108,7 +108,7 @@ public final class PlayerCommands { int item = Integer.parseInt(args.get(1)); int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerById(target); + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); if(targetPlayer == null) { CommandHandler.sendMessage(null, "Player not found."); return; } @@ -233,7 +233,7 @@ public final class PlayerCommands { int playerUid = Integer.parseInt(args.get(0)); int sceneId = Integer.parseInt(args.get(1)); - GenshinPlayer player = Grasscutter.getGameServer().getPlayerById(playerUid); + GenshinPlayer player = Grasscutter.getGameServer().getPlayerByUid(playerUid); if (player == null) { CommandHandler.sendMessage(null, "Player not found or offline."); return; diff --git a/src/main/java/emu/grasscutter/commands/ServerCommands.java b/src/main/java/emu/grasscutter/commands/ServerCommands.java index 1262fae97..7aaf1ae5b 100644 --- a/src/main/java/emu/grasscutter/commands/ServerCommands.java +++ b/src/main/java/emu/grasscutter/commands/ServerCommands.java @@ -43,7 +43,7 @@ public final class ServerCommands { int target = Integer.parseInt(args.get(0)); String message = String.join(" ", args.subList(1, args.size())); - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerById(target); + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); if(targetPlayer == null) { CommandHandler.sendMessage(null, "Player not found."); return; } @@ -65,7 +65,7 @@ public final class ServerCommands { int target = Integer.parseInt(args.get(0)); String message = String.join(" ", args.subList(1, args.size())); - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerById(target); + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); if(targetPlayer == null) { CommandHandler.sendMessage(player, "Player not found."); return; } diff --git a/src/main/java/emu/grasscutter/database/DatabaseHelper.java b/src/main/java/emu/grasscutter/database/DatabaseHelper.java index 905c4ef64..0289c3e71 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseHelper.java +++ b/src/main/java/emu/grasscutter/database/DatabaseHelper.java @@ -124,13 +124,13 @@ public class DatabaseHelper { int id = 0; if (reservedId > 0 && !checkPlayerExists(reservedId)) { id = reservedId; - character.setId(id); + character.setUid(id); } else { do { id = DatabaseManager.getNextId(character); } while (checkPlayerExists(id)); - character.setId(id); + character.setUid(id); } // Save to database DatabaseManager.getDatastore().save(character); @@ -160,7 +160,7 @@ public class DatabaseHelper { } public static List getAvatars(GenshinPlayer player) { - Query query = DatabaseManager.getDatastore().createQuery(GenshinAvatar.class).filter("ownerId", player.getId()); + Query query = DatabaseManager.getDatastore().createQuery(GenshinAvatar.class).filter("ownerId", player.getUid()); return query.find().toList(); } @@ -174,16 +174,16 @@ public class DatabaseHelper { } public static List getInventoryItems(GenshinPlayer player) { - Query query = DatabaseManager.getDatastore().createQuery(GenshinItem.class).filter("ownerId", player.getId()); + Query query = DatabaseManager.getDatastore().createQuery(GenshinItem.class).filter("ownerId", player.getUid()); return query.find().toList(); } public static List getFriends(GenshinPlayer player) { - Query query = DatabaseManager.getDatastore().createQuery(Friendship.class).filter("ownerId", player.getId()); + Query query = DatabaseManager.getDatastore().createQuery(Friendship.class).filter("ownerId", player.getUid()); return query.find().toList(); } public static List getReverseFriends(GenshinPlayer player) { - Query query = DatabaseManager.getDatastore().createQuery(Friendship.class).filter("friendId", player.getId()); + Query query = DatabaseManager.getDatastore().createQuery(Friendship.class).filter("friendId", player.getUid()); return query.find().toList(); } diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 4a5b585a5..70fea04f0 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -156,17 +156,17 @@ public class GenshinPlayer { this.getRotation().set(0, 307, 0); } - public int getId() { + public int getUid() { return id; } - public void setId(int id) { + public void setUid(int id) { this.id = id; } - public long getNextGuid() { + public long getNextGenshinGuid() { long nextId = ++this.nextGuid; - return ((long) this.getId() << 32) + nextId; + return ((long) this.getUid() << 32) + nextId; } public Account getAccount() { @@ -175,7 +175,7 @@ public class GenshinPlayer { public void setAccount(Account account) { this.account = account; - this.account.setPlayerId(getId()); + this.account.setPlayerId(getUid()); } public GameSession getSession() { @@ -560,7 +560,7 @@ public class GenshinPlayer { } public void dropMessage(Object message) { - this.sendPacket(new PacketPrivateChatNotify(GenshinConstants.SERVER_CONSOLE_UID, getId(), message.toString())); + this.sendPacket(new PacketPrivateChatNotify(GenshinConstants.SERVER_CONSOLE_UID, getUid(), message.toString())); } /** @@ -569,7 +569,7 @@ public class GenshinPlayer { * @param message The message to send. */ public void sendMessage(GenshinPlayer sender, Object message) { - this.sendPacket(new PacketPrivateChatNotify(sender.getId(), this.getId(), message.toString())); + this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString())); } public void interactWith(int gadgetEntityId) { @@ -614,7 +614,7 @@ public class GenshinPlayer { public OnlinePlayerInfo getOnlinePlayerInfo() { OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder() - .setUid(this.getId()) + .setUid(this.getUid()) .setNickname(this.getNickname()) .setPlayerLevel(this.getLevel()) .setMpSettingType(this.getMpSetting()) @@ -633,7 +633,7 @@ public class GenshinPlayer { public SocialDetail.Builder getSocialDetail() { SocialDetail.Builder social = SocialDetail.newBuilder() - .setUid(this.getId()) + .setUid(this.getUid()) .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())) .setNickname(this.getNickname()) .setSignature(this.getSignature()) @@ -649,7 +649,7 @@ public class GenshinPlayer { public PlayerLocationInfo getPlayerLocationInfo() { return PlayerLocationInfo.newBuilder() - .setUid(this.getId()) + .setUid(this.getUid()) .setPos(this.getPos().toProto()) .setRot(this.getRotation().toProto()) .build(); @@ -699,7 +699,7 @@ public class GenshinPlayer { // Check if player object exists in server // TODO - optimize - GenshinPlayer exists = this.getServer().getPlayerById(getId()); + GenshinPlayer exists = this.getServer().getPlayerByUid(getUid()); if (exists != null) { exists.getSession().close(); } diff --git a/src/main/java/emu/grasscutter/game/TeamInfo.java b/src/main/java/emu/grasscutter/game/TeamInfo.java index 24b8aa60d..0a12b1d2c 100644 --- a/src/main/java/emu/grasscutter/game/TeamInfo.java +++ b/src/main/java/emu/grasscutter/game/TeamInfo.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import emu.grasscutter.GenshinConstants; +import emu.grasscutter.Grasscutter; import emu.grasscutter.game.avatar.GenshinAvatar; public class TeamInfo { @@ -12,7 +13,7 @@ public class TeamInfo { public TeamInfo() { this.name = ""; - this.avatars = new ArrayList<>(GenshinConstants.MAX_AVATARS_IN_TEAM); + this.avatars = new ArrayList<>(Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam); } public String getName() { @@ -36,7 +37,7 @@ public class TeamInfo { } public boolean addAvatar(GenshinAvatar avatar) { - if (size() >= GenshinConstants.MAX_AVATARS_IN_TEAM || contains(avatar)) { + if (size() >= Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam || contains(avatar)) { return false; } @@ -56,7 +57,7 @@ public class TeamInfo { } public void copyFrom(TeamInfo team) { - copyFrom(team, GenshinConstants.MAX_AVATARS_IN_TEAM); + copyFrom(team, Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam); } public void copyFrom(TeamInfo team, int maxTeamSize) { diff --git a/src/main/java/emu/grasscutter/game/TeamManager.java b/src/main/java/emu/grasscutter/game/TeamManager.java index 3f6c4ebe5..4cd7bfcdf 100644 --- a/src/main/java/emu/grasscutter/game/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/TeamManager.java @@ -10,6 +10,7 @@ import java.util.Set; import dev.morphia.annotations.Transient; import emu.grasscutter.GenshinConstants; +import emu.grasscutter.Grasscutter; import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.game.avatar.GenshinAvatar; import emu.grasscutter.game.entity.EntityAvatar; @@ -163,12 +164,13 @@ public class TeamManager { public int getMaxTeamSize() { if (getPlayer().isInMultiplayer()) { + int max = Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeamMultiplayer; if (getPlayer().getWorld().getHost() == this.getPlayer()) { - return Math.max(1, (int) Math.ceil(GenshinConstants.MAX_AVATARS_IN_TEAM / (double) getWorld().getPlayerCount())); + return Math.max(1, (int) Math.ceil(max / (double) getWorld().getPlayerCount())); } - return Math.max(1, (int) Math.floor(GenshinConstants.MAX_AVATARS_IN_TEAM / (double) getWorld().getPlayerCount())); + return Math.max(1, (int) Math.floor(max / (double) getWorld().getPlayerCount())); } - return GenshinConstants.MAX_AVATARS_IN_TEAM; + return Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam; } // Methods diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index 414c73cfa..ba3b46554 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -148,8 +148,8 @@ public class GenshinAvatar { public void setOwner(GenshinPlayer player) { this.owner = player; - this.ownerId = player.getId(); - this.guid = player.getNextGuid(); + this.ownerId = player.getUid(); + this.guid = player.getNextGenshinGuid(); } public int getSatiation() { diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index e82624ba7..86e8ea458 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -107,7 +107,7 @@ public class EntityAvatar extends GenshinEntity { public SceneAvatarInfo getSceneAvatarInfo() { SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() - .setPlayerId(this.getPlayer().getId()) + .setPlayerId(this.getPlayer().getUid()) .setAvatarId(this.getAvatar().getAvatarId()) .setGuid(this.getAvatar().getGuid()) .setPeerId(this.getPlayer().getPeerId()) diff --git a/src/main/java/emu/grasscutter/game/entity/EntityItem.java b/src/main/java/emu/grasscutter/game/entity/EntityItem.java index e96098d29..ba1188f13 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityItem.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityItem.java @@ -36,7 +36,7 @@ public class EntityItem extends EntityGadget { this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.pos = new Position(pos); this.rot = new Position(); - this.guid = player.getNextGuid(); + this.guid = player.getNextGenshinGuid(); this.item = new GenshinItem(itemData, count); } diff --git a/src/main/java/emu/grasscutter/game/friends/FriendsList.java b/src/main/java/emu/grasscutter/game/friends/FriendsList.java index 26c65f26a..582a10efd 100644 --- a/src/main/java/emu/grasscutter/game/friends/FriendsList.java +++ b/src/main/java/emu/grasscutter/game/friends/FriendsList.java @@ -79,11 +79,11 @@ public class FriendsList { } // Make sure asker cant do anything - if (myFriendship.getAskerId() == this.getPlayer().getId()) { + if (myFriendship.getAskerId() == this.getPlayer().getUid()) { return; } - GenshinPlayer target = getPlayer().getSession().getServer().forceGetPlayerById(targetUid); + GenshinPlayer target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true); if (target == null) { return; // Should never happen } @@ -91,7 +91,7 @@ public class FriendsList { // Get target's friendship Friendship theirFriendship = null; if (target.isOnline()) { - theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getId()); + theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid()); } else { theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship); } @@ -112,7 +112,7 @@ public class FriendsList { this.addFriend(myFriendship); if (target.isOnline()) { - target.getFriendsList().getPendingFriends().remove(this.getPlayer().getId()); + target.getFriendsList().getPendingFriends().remove(this.getPlayer().getUid()); target.getFriendsList().addFriend(theirFriendship); } @@ -124,7 +124,7 @@ public class FriendsList { myFriendship.delete(); // Delete from target uid if (target.isOnline()) { - theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getId()); + theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid()); } theirFriendship.delete(); } @@ -146,7 +146,7 @@ public class FriendsList { GenshinPlayer friend = myFriendship.getFriendProfile().getPlayer(); if (friend != null) { // Friend online - theirFriendship = friend.getFriendsList().getFriendById(this.getPlayer().getId()); + theirFriendship = friend.getFriendsList().getFriendById(this.getPlayer().getUid()); if (theirFriendship != null) { friend.getFriendsList().getFriends().remove(theirFriendship.getFriendId()); theirFriendship.delete(); @@ -165,7 +165,7 @@ public class FriendsList { } public synchronized void sendFriendRequest(int targetUid) { - GenshinPlayer target = getPlayer().getSession().getServer().forceGetPlayerById(targetUid); + GenshinPlayer target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true); if (target == null || target == this.getPlayer()) { return; @@ -220,14 +220,14 @@ public class FriendsList { friendship.setOwner(getPlayer()); // Check if friend is online - GenshinPlayer friend = getPlayer().getSession().getServer().getPlayerById(friendship.getFriendProfile().getId()); + GenshinPlayer friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getId()); if (friend != null) { // Set friend to online mode friendship.setFriendProfile(friend); // Update our status on friend's client if theyre online if (friend.getFriendsList().hasLoaded()) { - Friendship theirFriendship = friend.getFriendsList().getFriendshipById(getPlayer().getId()); + Friendship theirFriendship = friend.getFriendsList().getFriendshipById(getPlayer().getUid()); if (theirFriendship != null) { // Update friend profile theirFriendship.setFriendProfile(getPlayer()); diff --git a/src/main/java/emu/grasscutter/game/friends/Friendship.java b/src/main/java/emu/grasscutter/game/friends/Friendship.java index 954ab318c..2be7d1fce 100644 --- a/src/main/java/emu/grasscutter/game/friends/Friendship.java +++ b/src/main/java/emu/grasscutter/game/friends/Friendship.java @@ -27,10 +27,10 @@ public class Friendship { public Friendship(GenshinPlayer owner, GenshinPlayer friend, GenshinPlayer asker) { this.setOwner(owner); - this.ownerId = owner.getId(); - this.friendId = friend.getId(); + this.ownerId = owner.getUid(); + this.friendId = friend.getUid(); this.profile = friend.getProfile(); - this.askerId = asker.getId(); + this.askerId = asker.getUid(); } public GenshinPlayer getOwner() { @@ -70,7 +70,7 @@ public class Friendship { } public void setFriendProfile(GenshinPlayer character) { - if (character == null || this.friendId != character.getId()) return; + if (character == null || this.friendId != character.getUid()) return; this.profile = character.getProfile(); } diff --git a/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java b/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java index 647557e0c..cc7408aa6 100644 --- a/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java +++ b/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java @@ -22,7 +22,7 @@ public class PlayerProfile { public PlayerProfile() { } public PlayerProfile(GenshinPlayer player) { - this.id = player.getId(); + this.id = player.getUid(); this.syncWithCharacter(player); } diff --git a/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java b/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java index b9914644d..0b0db49c4 100644 --- a/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java +++ b/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java @@ -125,8 +125,8 @@ public class GenshinItem { } public void setOwner(GenshinPlayer player) { - this.ownerId = player.getId(); - this.guid = player.getNextGuid(); + this.ownerId = player.getUid(); + this.guid = player.getNextGenshinGuid(); } public int getItemId() { return itemId; diff --git a/src/main/java/emu/grasscutter/game/inventory/Inventory.java b/src/main/java/emu/grasscutter/game/inventory/Inventory.java index 5629521c8..19bdcc525 100644 --- a/src/main/java/emu/grasscutter/game/inventory/Inventory.java +++ b/src/main/java/emu/grasscutter/game/inventory/Inventory.java @@ -6,6 +6,7 @@ import java.util.LinkedList; import java.util.List; import emu.grasscutter.GenshinConstants; +import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.def.AvatarCostumeData; import emu.grasscutter.data.def.AvatarData; @@ -36,10 +37,10 @@ public class Inventory implements Iterable { this.store = new Long2ObjectOpenHashMap<>(); this.inventoryTypes = new Int2ObjectOpenHashMap<>(); - this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(GenshinConstants.LIMIT_WEAPON)); - this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(GenshinConstants.LIMIT_RELIC)); - this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(GenshinConstants.LIMIT_MATERIAL)); - this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(GenshinConstants.LIMIT_FURNITURE)); + this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitWeapon)); + this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitRelic)); + this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitMaterial)); + this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitFurniture)); } public GenshinPlayer getPlayer() { diff --git a/src/main/java/emu/grasscutter/game/managers/ChatManager.java b/src/main/java/emu/grasscutter/game/managers/ChatManager.java index 3856c0fd1..40b1a55b5 100644 --- a/src/main/java/emu/grasscutter/game/managers/ChatManager.java +++ b/src/main/java/emu/grasscutter/game/managers/ChatManager.java @@ -36,14 +36,14 @@ public class ChatManager { } // Get target - GenshinPlayer target = getServer().getPlayerById(targetUid); + GenshinPlayer target = getServer().getPlayerByUid(targetUid); if (target == null) { return; } // Create chat packet - GenshinPacket packet = new PacketPrivateChatNotify(player.getId(), target.getId(), message); + GenshinPacket packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), message); player.sendPacket(packet); target.sendPacket(packet); @@ -51,14 +51,14 @@ public class ChatManager { public void sendPrivateMessage(GenshinPlayer player, int targetUid, int emote) { // Get target - GenshinPlayer target = getServer().getPlayerById(targetUid); + GenshinPlayer target = getServer().getPlayerByUid(targetUid); if (target == null) { return; } // Create chat packet - GenshinPacket packet = new PacketPrivateChatNotify(player.getId(), target.getId(), emote); + GenshinPacket packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote); player.sendPacket(packet); target.sendPacket(packet); diff --git a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java index 0e0562421..56d629036 100644 --- a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java +++ b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java @@ -24,7 +24,7 @@ public class MultiplayerManager { } public void applyEnterMp(GenshinPlayer player, int targetUid) { - GenshinPlayer target = getServer().getPlayerById(targetUid); + GenshinPlayer target = getServer().getPlayerByUid(targetUid); if (target == null) { player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.PlayerCannotEnterMp)); return; @@ -43,7 +43,7 @@ public class MultiplayerManager { */ // Get request - CoopRequest request = target.getCoopRequests().get(player.getId()); + CoopRequest request = target.getCoopRequests().get(player.getUid()); if (request != null && !request.isExpired()) { // Join request already exists @@ -52,7 +52,7 @@ public class MultiplayerManager { // Put request in request = new CoopRequest(player); - target.getCoopRequests().put(player.getId(), request); + target.getCoopRequests().put(player.getUid(), request); // Packet target.sendPacket(new PacketPlayerApplyEnterMpNotify(player)); @@ -137,7 +137,7 @@ public class MultiplayerManager { } // Get victim and sanity checks - GenshinPlayer victim = player.getServer().getPlayerById(targetUid); + GenshinPlayer victim = player.getServer().getPlayerByUid(targetUid); if (victim == null || victim == player) { return false; diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 329a28e61..d54f57d23 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -104,21 +104,25 @@ public final class GameServer extends MihoyoKcpServer { } public void registerPlayer(GenshinPlayer player) { - getPlayers().put(player.getId(), player); + getPlayers().put(player.getUid(), player); } - public GenshinPlayer getPlayerById(int id) { - return this.getPlayers().get(id); + public GenshinPlayer getPlayerByUid(int id) { + return this.getPlayerByUid(id, false); } - public GenshinPlayer forceGetPlayerById(int id) { + public GenshinPlayer getPlayerByUid(int id, boolean allowOfflinePlayers) { // Console check if (id == GenshinConstants.SERVER_CONSOLE_UID) { return null; } // Get from online players - GenshinPlayer player = this.getPlayerById(id); + GenshinPlayer player = this.getPlayerByUid(id); + + if (!allowOfflinePlayers) { + return player; + } // Check database if character isnt here if (player == null) { @@ -128,9 +132,9 @@ public final class GameServer extends MihoyoKcpServer { return player; } - public SocialDetail.Builder getSocialDetailById(int id) { + public SocialDetail.Builder getSocialDetailByUid(int id) { // Get from online players - GenshinPlayer player = this.forceGetPlayerById(id); + GenshinPlayer player = this.getPlayerByUid(id, true); if (player == null) { return null; diff --git a/src/main/java/emu/grasscutter/server/game/GameSession.java b/src/main/java/emu/grasscutter/server/game/GameSession.java index 29dcd46b6..1a54f31ba 100644 --- a/src/main/java/emu/grasscutter/server/game/GameSession.java +++ b/src/main/java/emu/grasscutter/server/game/GameSession.java @@ -123,7 +123,7 @@ public class GameSession extends MihoyoKcpChannel { // Save getPlayer().onLogout(); // Remove from gameserver - getServer().getPlayers().remove(getPlayer().getId()); + getServer().getPlayers().remove(getPlayer().getUid()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerSocialDetailReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerSocialDetailReq.java index 8391f2352..eafd15b78 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerSocialDetailReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerSocialDetailReq.java @@ -15,7 +15,7 @@ public class HandlerGetPlayerSocialDetailReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { GetPlayerSocialDetailReq req = GetPlayerSocialDetailReq.parseFrom(payload); - SocialDetail.Builder detail = session.getServer().getSocialDetailById(req.getUid()); + SocialDetail.Builder detail = session.getServer().getSocialDetailByUid(req.getUid()); if (detail != null) { detail.setIsFriend(session.getPlayer().getFriendsList().isFriendsWith(req.getUid())); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java index a91da56ad..024728789 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java @@ -28,7 +28,7 @@ public class HandlerMarkMapReq extends PacketHandler { session.getPlayer().getPos().setZ(req.getMark().getPos().getZ()); session.getPlayer().getPos().setY(300); - Grasscutter.getLogger().info("Player [" + session.getPlayer().getId() + ":" + session.getPlayer().getNickname() + "] tp to " + session.getPlayer().getPos() + " Scene id: " + req.getMark().getSceneId()); + Grasscutter.getLogger().info("Player [" + session.getPlayer().getUid() + ":" + session.getPlayer().getNickname() + "] tp to " + session.getPlayer().getPos() + " Scene id: " + req.getMark().getSceneId()); if (req.getMark().getSceneId() != session.getPlayer().getSceneId()) { session.getPlayer().getWorld().transferPlayerToScene(session.getPlayer(), req.getMark().getSceneId(), session.getPlayer().getPos()); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java index a45cca5b0..5cbbd8d04 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java @@ -57,7 +57,7 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler { } // Save account - session.getAccount().setPlayerId(player.getId()); + session.getAccount().setPlayerId(player.getUid()); session.getAccount().save(); // Set character diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerAskFriendListRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerAskFriendListRsp.java index 14bd3432d..e0aa0601f 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerAskFriendListRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerAskFriendListRsp.java @@ -14,7 +14,7 @@ public class PacketGetPlayerAskFriendListRsp extends GenshinPacket { GetPlayerAskFriendListRsp.Builder proto = GetPlayerAskFriendListRsp.newBuilder(); for (Friendship friendship : player.getFriendsList().getPendingFriends().values()) { - if (friendship.getAskerId() == player.getId()) { + if (friendship.getAskerId() == player.getUid()) { continue; } proto.addAskFriendList(friendship.toProto()); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java index bf0630eee..9df89af5f 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java @@ -37,7 +37,7 @@ public class PacketGetPlayerFriendListRsp extends GenshinPacket { proto.addFriendList(friendship.toProto()); } for (Friendship friendship : player.getFriendsList().getPendingFriends().values()) { - if (friendship.getAskerId() == player.getId()) { + if (friendship.getAskerId() == player.getUid()) { continue; } proto.addAskFriendList(friendship.toProto()); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHostPlayerNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHostPlayerNotify.java index b291074d8..9a43124e4 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketHostPlayerNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHostPlayerNotify.java @@ -11,7 +11,7 @@ public class PacketHostPlayerNotify extends GenshinPacket { super(PacketOpcodes.HostPlayerNotify); HostPlayerNotify proto = HostPlayerNotify.newBuilder() - .setHostUid(world.getHost().getId()) + .setHostUid(world.getHost().getUid()) .setHostPeerId(world.getHost().getPeerId()) .build(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerApplyEnterMpResultNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerApplyEnterMpResultNotify.java index 2e94cc4d0..d2b5be7f5 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerApplyEnterMpResultNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerApplyEnterMpResultNotify.java @@ -12,7 +12,7 @@ public class PacketPlayerApplyEnterMpResultNotify extends GenshinPacket { super(PacketOpcodes.PlayerApplyEnterMpResultNotify); PlayerApplyEnterMpResultNotify proto = PlayerApplyEnterMpResultNotify.newBuilder() - .setTargetUid(target.getId()) + .setTargetUid(target.getUid()) .setTargetNickname(target.getNickname()) .setIsAgreed(isAgreed) .setReason(reason) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerChatNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerChatNotify.java index 482a00ae2..287292ee4 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerChatNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerChatNotify.java @@ -14,7 +14,7 @@ public class PacketPlayerChatNotify extends GenshinPacket { ChatInfo info = ChatInfo.newBuilder() .setTime((int) (System.currentTimeMillis() / 1000)) - .setUid(sender.getId()) + .setUid(sender.getUid()) .setText(message) .build(); @@ -31,7 +31,7 @@ public class PacketPlayerChatNotify extends GenshinPacket { ChatInfo info = ChatInfo.newBuilder() .setTime((int) (System.currentTimeMillis() / 1000)) - .setUid(sender.getId()) + .setUid(sender.getUid()) .setIcon(emote) .build(); @@ -48,7 +48,7 @@ public class PacketPlayerChatNotify extends GenshinPacket { ChatInfo info = ChatInfo.newBuilder() .setTime((int) (System.currentTimeMillis() / 1000)) - .setUid(sender.getId()) + .setUid(sender.getUid()) .setSystemHint(systemHint) .build(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java index d7c7361a9..69ca9cb5f 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java @@ -24,13 +24,13 @@ public class PacketPlayerEnterSceneNotify extends GenshinPacket { .setPos(player.getPos().toProto()) .setSceneBeginTime(System.currentTimeMillis()) .setType(EnterType.EnterSelf) - .setTargetUid(player.getId()) + .setTargetUid(player.getUid()) .setEnterSceneToken(player.getEnterSceneToken()) .setWorldLevel(player.getWorldLevel()) .setEnterReason(EnterReason.Login.getValue()) .setIsFirstLoginEnterScene(player.isFirstLoginEnterScene()) .setUnk1(1) - .setUnk2("3-" + player.getId() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402) + .setUnk2("3-" + player.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402) .build(); this.setData(proto); @@ -53,7 +53,7 @@ public class PacketPlayerEnterSceneNotify extends GenshinPacket { .setPos(newPos.toProto()) .setSceneBeginTime(System.currentTimeMillis()) .setType(EnterType.EnterSelf) - .setTargetUid(target.getId()) + .setTargetUid(target.getUid()) .setEnterSceneToken(player.getEnterSceneToken()) .setWorldLevel(target.getWorld().getWorldLevel()) .setEnterReason(reason.getValue()) @@ -62,7 +62,7 @@ public class PacketPlayerEnterSceneNotify extends GenshinPacket { .addSceneTagIdList(113) .addSceneTagIdList(117) .setUnk1(1) - .setUnk2(newScene + "-" + target.getId() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402) + .setUnk2(newScene + "-" + target.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerGameTimeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerGameTimeNotify.java index 32fa3194b..1dd219e1e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerGameTimeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerGameTimeNotify.java @@ -13,7 +13,7 @@ public class PacketPlayerGameTimeNotify extends GenshinPacket { PlayerGameTimeNotify proto = PlayerGameTimeNotify.newBuilder() .setGameTime(player.getScene().getTime()) - .setUid(player.getId()) + .setUid(player.getUid()) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java index 45a13325b..c21a15351 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java @@ -1,6 +1,7 @@ package emu.grasscutter.server.packet.send; import emu.grasscutter.GenshinConstants; +import emu.grasscutter.Grasscutter; import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.inventory.GenshinItem; import emu.grasscutter.net.packet.GenshinPacket; @@ -18,7 +19,7 @@ public class PacketPlayerStoreNotify extends GenshinPacket { PlayerStoreNotify.Builder p = PlayerStoreNotify.newBuilder() .setStoreType(StoreType.StorePack) - .setWeightLimit(GenshinConstants.LIMIT_ALL); + .setWeightLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitAll); for (GenshinItem item : player.getInventory()) { Item itemProto = item.toProto(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java index 34ca8b3b7..8048e4b0d 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java @@ -21,7 +21,7 @@ public class PacketPullRecentChatRsp extends GenshinPacket { ChatInfo welcomeEmote = ChatInfo.newBuilder() .setTime((int) (System.currentTimeMillis() / 1000)) .setUid(GenshinConstants.SERVER_CONSOLE_UID) - .setToUid(player.getId()) + .setToUid(player.getUid()) .setIcon(serverOptions.WelcomeEmotes[Utils.randomRange(0, serverOptions.WelcomeEmotes.length - 1)]) .build(); @@ -32,7 +32,7 @@ public class PacketPullRecentChatRsp extends GenshinPacket { ChatInfo welcomeMotd = ChatInfo.newBuilder() .setTime((int) (System.currentTimeMillis() / 1000)) .setUid(GenshinConstants.SERVER_CONSOLE_UID) - .setToUid(player.getId()) + .setToUid(player.getUid()) .setText(Grasscutter.getConfig().getServerOptions().WelcomeMotd) .build(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerInfoNotify.java index a653889e2..0fa20784e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerInfoNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerInfoNotify.java @@ -18,7 +18,7 @@ public class PacketScenePlayerInfoNotify extends GenshinPacket { GenshinPlayer p = world.getPlayers().get(i); ScenePlayerInfo pInfo = ScenePlayerInfo.newBuilder() - .setUid(p.getId()) + .setUid(p.getUid()) .setPeerId(p.getPeerId()) .setName(p.getNickname()) .setSceneId(p.getSceneId()) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTeamUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTeamUpdateNotify.java index 1dac583ce..20e199bc4 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTeamUpdateNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTeamUpdateNotify.java @@ -19,7 +19,7 @@ public class PacketSceneTeamUpdateNotify extends GenshinPacket { for (GenshinPlayer p : player.getWorld().getPlayers()) { for (EntityAvatar entityAvatar : p.getTeamManager().getActiveTeam()) { SceneTeamAvatar.Builder avatarProto = SceneTeamAvatar.newBuilder() - .setPlayerId(p.getId()) + .setPlayerId(p.getUid()) .setAvatarGuid(entityAvatar.getAvatar().getGuid()) .setSceneId(p.getSceneId()) .setEntityId(entityAvatar.getId()) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java index ae4418cb6..b93130388 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java @@ -1,6 +1,6 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.GenshinConstants; +import emu.grasscutter.Grasscutter; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.StoreTypeOuterClass.StoreType; @@ -13,11 +13,11 @@ public class PacketStoreWeightLimitNotify extends GenshinPacket { StoreWeightLimitNotify p = StoreWeightLimitNotify.newBuilder() .setStoreType(StoreType.StorePack) - .setWeightLimit(GenshinConstants.LIMIT_ALL) - .setWeaponCountLimit(GenshinConstants.LIMIT_WEAPON) - .setReliquaryCountLimit(GenshinConstants.LIMIT_RELIC) - .setMaterialCountLimit(GenshinConstants.LIMIT_MATERIAL) - .setFurnitureCountLimit(GenshinConstants.LIMIT_FURNITURE) + .setWeightLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitAll) + .setWeaponCountLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitWeapon) + .setReliquaryCountLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitRelic) + .setMaterialCountLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitMaterial) + .setFurnitureCountLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitFurniture) .build(); this.setData(p); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerInfoNotify.java index c14c7e43d..709a128d2 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerInfoNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerInfoNotify.java @@ -17,7 +17,7 @@ public class PacketWorldPlayerInfoNotify extends GenshinPacket { GenshinPlayer p = world.getPlayers().get(i); proto.addPlayerInfoList(p.getOnlinePlayerInfo()); - proto.addPlayerUidList(p.getId()); + proto.addPlayerUidList(p.getUid()); } this.setData(proto.build()); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerRTTNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerRTTNotify.java index 20b64e418..1154b3ee3 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerRTTNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerRTTNotify.java @@ -17,7 +17,7 @@ public class PacketWorldPlayerRTTNotify extends GenshinPacket { for (GenshinPlayer player : world.getPlayers()) { proto.addPlayerRttList( PlayerRTTInfo.newBuilder() - .setUid(player.getId()) + .setUid(player.getUid()) .setRtt(10) // TODO - put player ping here ); } diff --git a/src/main/java/emu/grasscutter/utils/Utils.java b/src/main/java/emu/grasscutter/utils/Utils.java index 7c5ab18ba..251898bd4 100644 --- a/src/main/java/emu/grasscutter/utils/Utils.java +++ b/src/main/java/emu/grasscutter/utils/Utils.java @@ -165,8 +165,6 @@ public final class Utils { // Check for game data. if(!fileExists(dataFolder)) createFolder(dataFolder); - if(!fileExists(dataFolder + "AbilityEmbryos.json")) - copyFromResources("data/AbilityEmbryos.json", dataFolder); if(exit) System.exit(1); } From f74ca6e9a036c0a99ce2508a4cc9054e84f1d9fc Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Tue, 19 Apr 2022 18:55:32 +0800 Subject: [PATCH 15/17] getAwakenMaterial Add --- src/main/java/emu/grasscutter/data/def/ItemData.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/data/def/ItemData.java b/src/main/java/emu/grasscutter/data/def/ItemData.java index 6c87afead..25be666e7 100644 --- a/src/main/java/emu/grasscutter/data/def/ItemData.java +++ b/src/main/java/emu/grasscutter/data/def/ItemData.java @@ -48,6 +48,7 @@ public class ItemData extends GenshinResource { private int WeaponBaseExp; private int StoryId; private int AvatarPromoteId; + private int AwakenMaterial; private int[] AwakenCosts; private int[] SkillAffix; private WeaponProperty[] WeaponProp; @@ -160,6 +161,10 @@ public class ItemData extends GenshinResource { return WeaponBaseExp; } + public int getAwakenMaterial() { + return AwakenMaterial; + } + public int[] getAwakenCosts() { return AwakenCosts; } @@ -250,4 +255,4 @@ public class ItemData extends GenshinResource { } } -} \ No newline at end of file +} From 3da79a1dc1990264b9d89fbb9b616f2219c08ca6 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Tue, 19 Apr 2022 18:59:43 +0800 Subject: [PATCH 16/17] Fix refine --- .../grasscutter/game/managers/InventoryManager.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index 282cd2cd8..3d56dfad2 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -438,8 +438,14 @@ public class InventoryManager { return; } - if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemId() != feed.getItemId()) { - return; + if (weapon.getItemData().getAwakenMaterial() == 0) { + if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemId() != feed.getItemId()) { + return; + } + } else { + if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemData().getAwakenMaterial() != feed.getItemId()) { + return; + } } if (weapon.getRefinement() >= 4 || weapon.getAffixes() == null || weapon.getAffixes().size() == 0) { From aedfb59831dc7e59f81b91879387f7f63b49cdc1 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Tue, 19 Apr 2022 04:13:38 -0700 Subject: [PATCH 17/17] Remove duplicate function and fix stackoverflow issue --- src/main/java/emu/grasscutter/game/GenshinPlayer.java | 9 --------- .../java/emu/grasscutter/server/game/GameServer.java | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index e29a6dcc4..70fea04f0 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -571,15 +571,6 @@ public class GenshinPlayer { public void sendMessage(GenshinPlayer sender, Object message) { this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString())); } - - /** - * Sends a message to another player. - * @param sender The sender of the message. - * @param message The message to send. - */ - public void sendMessage(GenshinPlayer sender, Object message) { - this.sendPacket(new PacketPrivateChatNotify(sender.getId(), this.getId(), message.toString())); - } public void interactWith(int gadgetEntityId) { GenshinEntity entity = getScene().getEntityById(gadgetEntityId); diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index d54f57d23..b42ced55c 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -118,7 +118,7 @@ public final class GameServer extends MihoyoKcpServer { } // Get from online players - GenshinPlayer player = this.getPlayerByUid(id); + GenshinPlayer player = this.getPlayers().get(id); if (!allowOfflinePlayers) { return player;