Implement support for multiple scenes in a world

This commit is contained in:
Melledy 2022-04-18 09:39:29 -07:00
parent eaba8bc1b5
commit bee654c64f
30 changed files with 487 additions and 331 deletions

View File

@ -170,12 +170,12 @@ public class PlayerCommands {
float range = (5f + (.1f * count)); float range = (5f + (.1f * count));
for (int i = 0; i < count; i++) { 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)); 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); EntityItem entity = new EntityItem(player.getScene(), player, itemData, pos, 1);
player.getWorld().addEntity(entity); player.getScene().addEntity(entity);
} }
} else { } else {
EntityItem entity = new EntityItem(player.getWorld(), player, itemData, player.getPos().clone().addY(3f), count); EntityItem entity = new EntityItem(player.getScene(), player, itemData, player.getPos().clone().addY(3f), count);
player.getWorld().addEntity(entity); player.getScene().addEntity(entity);
} }
} }
} }
@ -216,8 +216,8 @@ public class PlayerCommands {
float range = (5f + (.1f * count)); float range = (5f + (.1f * count));
for (int i = 0; i < count; i++) { 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)); 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); EntityMonster entity = new EntityMonster(player.getScene(), monsterData, pos, level);
player.getWorld().addEntity(entity); player.getScene().addEntity(entity);
} }
} }
} }
@ -227,12 +227,12 @@ public class PlayerCommands {
@Override @Override
public void execute(GenshinPlayer player, String raw) { public void execute(GenshinPlayer player, String raw) {
List<GenshinEntity> toRemove = new LinkedList<>(); List<GenshinEntity> toRemove = new LinkedList<>();
for (GenshinEntity entity : player.getWorld().getEntities().values()) { for (GenshinEntity entity : player.getScene().getEntities().values()) {
if (entity instanceof EntityMonster) { if (entity instanceof EntityMonster) {
toRemove.add(entity); 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.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));
} }
} }

View File

@ -83,6 +83,7 @@ public class GenshinPlayer {
@Transient private long nextGuid = 0; @Transient private long nextGuid = 0;
@Transient private int peerId; @Transient private int peerId;
@Transient private World world; @Transient private World world;
@Transient private GenshinScene scene;
@Transient private GameSession session; @Transient private GameSession session;
@Transient private AvatarStorage avatars; @Transient private AvatarStorage avatars;
@Transient private Inventory inventory; @Transient private Inventory inventory;
@ -206,6 +207,14 @@ public class GenshinPlayer {
this.world = world; this.world = world;
} }
public GenshinScene getScene() {
return scene;
}
public void setScene(GenshinScene scene) {
this.scene = scene;
}
public int getGmLevel() { public int getGmLevel() {
return 1; return 1;
} }
@ -560,14 +569,14 @@ public class GenshinPlayer {
} }
public void interactWith(int gadgetEntityId) { public void interactWith(int gadgetEntityId) {
GenshinEntity entity = getWorld().getEntityById(gadgetEntityId); GenshinEntity entity = getScene().getEntityById(gadgetEntityId);
if (entity == null) { if (entity == null) {
return; return;
} }
// Delete // Delete
entity.getWorld().removeEntity(entity); entity.getScene().removeEntity(entity);
// Handle // Handle
if (entity instanceof EntityItem) { if (entity instanceof EntityItem) {

View File

@ -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<GenshinPlayer> players;
private final Int2ObjectMap<GenshinEntity> 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<GenshinPlayer> getPlayers() {
return players;
}
public int getPlayerCount() {
return this.getPlayers().size();
}
public Int2ObjectMap<GenshinEntity> 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<EntityAvatar> 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<GenshinEntity> 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<GenshinEntity> 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);
}
}
}

View File

@ -46,12 +46,12 @@ public class InvokeHandler<T> {
try { try {
if (entryListForwardAll.size() > 0) { if (entryListForwardAll.size() > 0) {
GenshinPacket packet = packetClass.getDeclaredConstructor(List.class).newInstance(this.entryListForwardAll); GenshinPacket packet = packetClass.getDeclaredConstructor(List.class).newInstance(this.entryListForwardAll);
player.getWorld().broadcastPacket(packet); player.getScene().broadcastPacket(packet);
this.entryListForwardAll.clear(); this.entryListForwardAll.clear();
} }
if (entryListForwardAllExceptCur.size() > 0) { if (entryListForwardAllExceptCur.size() > 0) {
GenshinPacket packet = packetClass.getDeclaredConstructor(List.class).newInstance(this.entryListForwardAllExceptCur); GenshinPacket packet = packetClass.getDeclaredConstructor(List.class).newInstance(this.entryListForwardAllExceptCur);
player.getWorld().broadcastPacketToOthers(player, packet); player.getScene().broadcastPacketToOthers(player, packet);
this.entryListForwardAllExceptCur.clear(); this.entryListForwardAllExceptCur.clear();
} }
if (entryListForwardHost.size() > 0) { if (entryListForwardHost.size() > 0) {

View File

@ -158,7 +158,7 @@ public class TeamManager {
} }
public boolean isSpawned() { 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() { public int getMaxTeamSize() {
@ -233,7 +233,7 @@ public class TeamManager {
prevSelectedAvatarIndex = i; prevSelectedAvatarIndex = i;
} }
} else { } else {
entity = new EntityAvatar(getPlayer().getWorld(), getPlayer().getAvatars().getAvatarById(avatarId)); entity = new EntityAvatar(getPlayer().getScene(), getPlayer().getAvatars().getAvatarById(avatarId));
} }
this.getActiveTeam().add(entity); this.getActiveTeam().add(entity);
@ -241,7 +241,7 @@ public class TeamManager {
// Unload removed entities // Unload removed entities
for (EntityAvatar entity : existingAvatars.values()) { for (EntityAvatar entity : existingAvatars.values()) {
getPlayer().getWorld().removeEntity(entity); getPlayer().getScene().removeEntity(entity);
entity.getAvatar().save(); entity.getAvatar().save();
} }
@ -256,7 +256,7 @@ public class TeamManager {
updateTeamResonances(); updateTeamResonances();
// Packets // Packets
getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer())); getPlayer().getScene().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer()));
// Run callback // Run callback
if (responsePacket != null) { if (responsePacket != null) {
@ -266,7 +266,7 @@ public class TeamManager {
// Check if character changed // Check if character changed
if (currentEntity != getCurrentAvatarEntity()) { if (currentEntity != getCurrentAvatarEntity()) {
// Remove and Add // Remove and Add
getWorld().replaceEntity(currentEntity, getCurrentAvatarEntity()); getPlayer().getScene().replaceEntity(currentEntity, getCurrentAvatarEntity());
} }
} }
@ -396,7 +396,7 @@ public class TeamManager {
oldEntity.setMotionState(MotionState.MotionStandby); oldEntity.setMotionState(MotionState.MotionStandby);
// Remove and Add // Remove and Add
getWorld().replaceEntity(oldEntity, newEntity); getPlayer().getScene().replaceEntity(oldEntity, newEntity);
getPlayer().sendPacket(new PacketChangeAvatarRsp(guid)); getPlayer().sendPacket(new PacketChangeAvatarRsp(guid));
} }
@ -426,7 +426,7 @@ public class TeamManager {
} else { } else {
// Set index and spawn replacement member // Set index and spawn replacement member
this.setCurrentCharacterIndex(replaceIndex); this.setCurrentCharacterIndex(replaceIndex);
getWorld().addEntity(replacement); getPlayer().getScene().addEntity(replacement);
} }
// Response packet // Response packet

View File

@ -33,24 +33,21 @@ import emu.grasscutter.server.packet.send.PacketSyncScenePlayTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketWorldPlayerInfoNotify; import emu.grasscutter.server.packet.send.PacketWorldPlayerInfoNotify;
import emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify; 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.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class World implements Iterable<GenshinPlayer> { public class World implements Iterable<GenshinPlayer> {
private final GenshinPlayer owner; private final GenshinPlayer owner;
private final List<GenshinPlayer> players; private final List<GenshinPlayer> players;
private final Int2ObjectMap<GenshinScene> scenes;
private int levelEntityId; private int levelEntityId;
private int nextEntityId = 0; private int nextEntityId = 0;
private int nextPeerId = 0; private int nextPeerId = 0;
private final Int2ObjectMap<GenshinEntity> entities;
private int worldLevel; private int worldLevel;
private int sceneId;
private int time;
private ClimateType climate;
private boolean isMultiplayer; private boolean isMultiplayer;
private boolean isDungeon;
public World(GenshinPlayer player) { public World(GenshinPlayer player) {
this(player, false); this(player, false);
@ -59,11 +56,9 @@ public class World implements Iterable<GenshinPlayer> {
public World(GenshinPlayer player, boolean isMultiplayer) { public World(GenshinPlayer player, boolean isMultiplayer) {
this.owner = player; this.owner = player;
this.players = Collections.synchronizedList(new ArrayList<>()); this.players = Collections.synchronizedList(new ArrayList<>());
this.entities = new Int2ObjectOpenHashMap<>(); this.scenes = new Int2ObjectOpenHashMap<>();
this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL); this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
this.sceneId = player.getSceneId();
this.time = 8 * 60;
this.climate = ClimateType.CLIMATE_SUNNY;
this.worldLevel = player.getWorldLevel(); this.worldLevel = player.getWorldLevel();
this.isMultiplayer = isMultiplayer; this.isMultiplayer = isMultiplayer;
} }
@ -87,22 +82,6 @@ public class World implements Iterable<GenshinPlayer> {
return ++this.nextPeerId; 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() { public int getWorldLevel() {
return worldLevel; return worldLevel;
} }
@ -111,46 +90,30 @@ public class World implements Iterable<GenshinPlayer> {
this.worldLevel = worldLevel; this.worldLevel = worldLevel;
} }
public ClimateType getClimate() {
return climate;
}
public void setClimate(ClimateType climate) {
this.climate = climate;
}
public List<GenshinPlayer> getPlayers() { public List<GenshinPlayer> getPlayers() {
return players; return players;
} }
public Int2ObjectMap<GenshinScene> getScenes() {
return this.scenes;
}
public GenshinScene getSceneById(int sceneId) {
return getScenes().computeIfAbsent(sceneId, id -> new GenshinScene(this, id));
}
public int getPlayerCount() { public int getPlayerCount() {
return getPlayers().size(); return getPlayers().size();
} }
public Int2ObjectMap<GenshinEntity> getEntities() {
return this.entities;
}
public boolean isInWorld(GenshinEntity entity) {
return this.entities.containsKey(entity.getId());
}
public boolean isMultiplayer() { public boolean isMultiplayer() {
return isMultiplayer; return isMultiplayer;
} }
public boolean isDungeon() {
return isDungeon;
}
public int getNextEntityId(EntityIdType idType) { public int getNextEntityId(EntityIdType idType) {
return (idType.getId() << 24) + ++this.nextEntityId; return (idType.getId() << 24) + ++this.nextEntityId;
} }
public GenshinEntity getEntityById(int id) {
return this.entities.get(id);
}
public synchronized void addPlayer(GenshinPlayer player) { public synchronized void addPlayer(GenshinPlayer player) {
// Check if player already in // Check if player already in
if (getPlayers().contains(player)) { if (getPlayers().contains(player)) {
@ -166,11 +129,18 @@ public class World implements Iterable<GenshinPlayer> {
player.setWorld(this); player.setWorld(this);
getPlayers().add(player); getPlayers().add(player);
// Set player variables
player.setPeerId(this.getNextPeerId()); player.setPeerId(this.getNextPeerId());
player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM)); player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
// Setup team avatars // Copy main team to mp team
this.setupPlayerAvatars(player); 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 // Info packet for other players
if (this.getPlayers().size() > 1) { if (this.getPlayers().size() > 1) {
@ -191,18 +161,15 @@ public class World implements Iterable<GenshinPlayer> {
getPlayers().remove(player); getPlayers().remove(player);
player.setWorld(null); player.setWorld(null);
this.removePlayerAvatars(player); // Remove from scene
GenshinScene scene = this.getSceneById(player.getSceneId());
scene.removePlayer(player);
// Info packet for other players // Info packet for other players
if (this.getPlayers().size() > 0) { if (this.getPlayers().size() > 0) {
this.updatePlayerInfos(player); this.updatePlayerInfos(player);
} }
// Remove player gadgets
for (EntityGadget gadget : player.getTeamManager().getGadgets()) {
this.removeEntity(gadget);
}
// Disband world if host leaves // Disband world if host leaves
if (getHost() == player) { if (getHost() == player) {
List<GenshinPlayer> kicked = new ArrayList<>(this.getPlayers()); List<GenshinPlayer> kicked = new ArrayList<>(this.getPlayers());
@ -210,11 +177,24 @@ public class World implements Iterable<GenshinPlayer> {
World world = new World(victim); World world = new World(victim);
world.addPlayer(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) { private void updatePlayerInfos(GenshinPlayer paramPlayer) {
for (GenshinPlayer player : getPlayers()) { for (GenshinPlayer player : getPlayers()) {
// Dont send packets if player is loading in and filter out joining player // Dont send packets if player is loading in and filter out joining player
@ -239,185 +219,6 @@ public class World implements Iterable<GenshinPlayer> {
} }
} }
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<GenshinEntity> 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<EntityAvatar> 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<GenshinEntity> 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) { public void broadcastPacket(GenshinPacket packet) {
// Send to all players - might have to check if player has been sent data packets // Send to all players - might have to check if player has been sent data packets
for (GenshinPlayer player : this.getPlayers()) { for (GenshinPlayer player : this.getPlayers()) {
@ -425,21 +226,6 @@ public class World implements Iterable<GenshinPlayer> {
} }
} }
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() { public void close() {
} }

View File

@ -122,7 +122,7 @@ public class AvatarStorage implements Iterable<GenshinAvatar> {
entity = new EntityAvatar(avatar); entity = new EntityAvatar(avatar);
getPlayer().sendPacket(new PacketAvatarChangeCostumeNotify(entity)); getPlayer().sendPacket(new PacketAvatarChangeCostumeNotify(entity));
} else { } else {
getPlayer().getWorld().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity)); getPlayer().getScene().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity));
} }
// Done // Done

View File

@ -5,6 +5,7 @@ import emu.grasscutter.data.GenshinData;
import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.def.AvatarData;
import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.game.GenshinScene;
import emu.grasscutter.game.World; import emu.grasscutter.game.World;
import emu.grasscutter.game.avatar.GenshinAvatar; import emu.grasscutter.game.avatar.GenshinAvatar;
import emu.grasscutter.game.inventory.EquipType; import emu.grasscutter.game.inventory.EquipType;
@ -39,14 +40,14 @@ public class EntityAvatar extends GenshinEntity {
private PlayerDieType killedType; private PlayerDieType killedType;
private int killedBy; private int killedBy;
public EntityAvatar(World world, GenshinAvatar avatar) { public EntityAvatar(GenshinScene scene, GenshinAvatar avatar) {
super(world); super(scene);
this.avatar = avatar; this.avatar = avatar;
this.id = world.getNextEntityId(EntityIdType.AVATAR); this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
GenshinItem weapon = this.getAvatar().getWeapon(); GenshinItem weapon = this.getAvatar().getWeapon();
if (weapon != null) { 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()) .setLastMoveReliableSeq(this.getLastMoveReliableSeq())
.setLifeState(this.getLifeState().getValue()); .setLifeState(this.getLifeState().getValue());
if (this.getWorld() != null) { if (this.getScene() != null) {
entityInfo.setMotionInfo(this.getMotionInfo()); entityInfo.setMotionInfo(this.getMotionInfo());
} }

View File

@ -1,6 +1,7 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.game.GenshinScene;
import emu.grasscutter.game.World; import emu.grasscutter.game.World;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
@ -34,8 +35,8 @@ public class EntityClientGadget extends EntityGadget {
private int targetEntityId; private int targetEntityId;
private boolean asyncLoad; private boolean asyncLoad;
public EntityClientGadget(World world, GenshinPlayer player, EvtCreateGadgetNotify notify) { public EntityClientGadget(GenshinScene scene, GenshinPlayer player, EvtCreateGadgetNotify notify) {
super(world); super(scene);
this.owner = player; this.owner = player;
this.id = notify.getEntityId(); this.id = notify.getEntityId();
this.pos = new Position(notify.getInitPos()); this.pos = new Position(notify.getInitPos());

View File

@ -1,11 +1,12 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.game.GenshinScene;
import emu.grasscutter.game.World; import emu.grasscutter.game.World;
public abstract class EntityGadget extends GenshinEntity { public abstract class EntityGadget extends GenshinEntity {
public EntityGadget(World world) { public EntityGadget(GenshinScene scene) {
super(world); super(scene);
} }
public abstract int getGadgetId(); public abstract int getGadgetId();

View File

@ -2,6 +2,7 @@ package emu.grasscutter.game.entity;
import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.def.ItemData;
import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.game.GenshinScene;
import emu.grasscutter.game.World; import emu.grasscutter.game.World;
import emu.grasscutter.game.inventory.GenshinItem; import emu.grasscutter.game.inventory.GenshinItem;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
@ -30,9 +31,9 @@ public class EntityItem extends EntityGadget {
private final GenshinItem item; private final GenshinItem item;
private final long guid; private final long guid;
public EntityItem(World world, GenshinPlayer player, ItemData itemData, Position pos, int count) { public EntityItem(GenshinScene scene, GenshinPlayer player, ItemData itemData, Position pos, int count) {
super(world); super(scene);
this.id = world.getNextEntityId(EntityIdType.GADGET); this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos); this.pos = new Position(pos);
this.rot = new Position(); this.rot = new Position();
this.guid = player.getNextGuid(); this.guid = player.getNextGuid();

View File

@ -4,6 +4,7 @@ import emu.grasscutter.data.GenshinData;
import emu.grasscutter.data.common.PropGrowCurve; import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.def.MonsterCurveData; import emu.grasscutter.data.def.MonsterCurveData;
import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.game.GenshinScene;
import emu.grasscutter.game.World; import emu.grasscutter.game.World;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
@ -36,9 +37,9 @@ public class EntityMonster extends GenshinEntity {
private final int level; private final int level;
private int weaponEntityId; private int weaponEntityId;
public EntityMonster(World world, MonsterData monsterData, Position pos, int level) { public EntityMonster(GenshinScene scene, MonsterData monsterData, Position pos, int level) {
super(world); super(scene);
this.id = world.getNextEntityId(EntityIdType.MONSTER); this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
this.monsterData = monsterData; this.monsterData = monsterData;
this.fightProp = new Int2FloatOpenHashMap(); this.fightProp = new Int2FloatOpenHashMap();
this.pos = new Position(pos); this.pos = new Position(pos);
@ -48,7 +49,7 @@ public class EntityMonster extends GenshinEntity {
// Monster weapon // Monster weapon
if (getMonsterWeaponId() > 0) { if (getMonsterWeaponId() > 0) {
this.weaponEntityId = world.getNextEntityId(EntityIdType.WEAPON); this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON);
} }
this.recalcStats(); this.recalcStats();

View File

@ -1,5 +1,6 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.game.GenshinScene;
import emu.grasscutter.game.World; import emu.grasscutter.game.World;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.LifeState;
@ -12,14 +13,14 @@ import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public abstract class GenshinEntity { public abstract class GenshinEntity {
protected int id; protected int id;
private final World world; private final GenshinScene scene;
private MotionState moveState; private MotionState moveState;
private int lastMoveSceneTimeMs; private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq; private int lastMoveReliableSeq;
public GenshinEntity(World world) { public GenshinEntity(GenshinScene scene) {
this.world = world; this.scene = scene;
this.moveState = MotionState.MotionNone; this.moveState = MotionState.MotionNone;
} }
@ -28,7 +29,11 @@ public abstract class GenshinEntity {
} }
public World getWorld() { public World getWorld() {
return world; return this.getScene().getWorld();
}
public GenshinScene getScene() {
return this.scene;
} }
public boolean isAlive() { public boolean isAlive() {

View File

@ -35,10 +35,12 @@ public class MultiplayerManager {
return; return;
} }
/*
if (target.getWorld().isDungeon()) { if (target.getWorld().isDungeon()) {
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.SceneCannotEnter)); player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.SceneCannotEnter));
return; return;
} }
*/
// Get request // Get request
CoopRequest request = target.getCoopRequests().get(player.getId()); CoopRequest request = target.getCoopRequests().get(player.getId());
@ -90,14 +92,14 @@ public class MultiplayerManager {
world.addPlayer(player); world.addPlayer(player);
// Rejoin packet // 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 // Make requester join
player.getWorld().addPlayer(requester); player.getWorld().addPlayer(requester);
// Packet // 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.getPos().set(player.getPos());
requester.getRotation().set(player.getRotation()); requester.getRotation().set(player.getRotation());
} }
@ -120,7 +122,7 @@ public class MultiplayerManager {
world.addPlayer(player); world.addPlayer(player);
// Packet // 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; return true;
} }
@ -147,7 +149,7 @@ public class MultiplayerManager {
World world = new World(victim); World world = new World(victim);
world.addPlayer(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; return true;
} }
} }

View File

@ -958,6 +958,7 @@ public class PacketOpcodes {
public static final int SceneTimeNotify = 229; public static final int SceneTimeNotify = 229;
public static final int SceneTransToPointReq = 256; public static final int SceneTransToPointReq = 256;
public static final int SceneTransToPointRsp = 283; public static final int SceneTransToPointRsp = 283;
public static final int SceneUnlockInfoNotify = 3386;
public static final int SceneWeatherForcastReq = 3167; public static final int SceneWeatherForcastReq = 3167;
public static final int SceneWeatherForcastRsp = 3023; public static final int SceneWeatherForcastRsp = 3023;
public static final int SeaLampCoinNotify = 2028; public static final int SeaLampCoinNotify = 2028;

View File

@ -14,8 +14,8 @@ public class HandlerChangeGameTimeReq extends PacketHandler {
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ChangeGameTimeReq req = ChangeGameTimeReq.parseFrom(payload); ChangeGameTimeReq req = ChangeGameTimeReq.parseFrom(payload);
session.getPlayer().getWorld().changeTime(req.getGameTime()); session.getPlayer().getScene().changeTime(req.getGameTime());
session.getPlayer().sendPacket(new PacketChangeGameTimeRsp(session.getPlayer().getWorld())); session.getPlayer().sendPacket(new PacketChangeGameTimeRsp(session.getPlayer()));
} }
} }

View File

@ -22,12 +22,12 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
case CombatEvtBeingHit: case CombatEvtBeingHit:
// Handle damage // Handle damage
EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData()); EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData());
session.getPlayer().getWorld().handleAttack(hitInfo.getAttackResult()); session.getPlayer().getScene().handleAttack(hitInfo.getAttackResult());
break; break;
case EntityMove: case EntityMove:
// Handle movement // Handle movement
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData()); 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) { if (entity != null) {
entity.getPosition().set(moveInfo.getMotionInfo().getPos()); entity.getPosition().set(moveInfo.getMotionInfo().getPos());
entity.getRotation().set(moveInfo.getMotionInfo().getRot()); entity.getRotation().set(moveInfo.getMotionInfo().getRot());

View File

@ -24,10 +24,10 @@ public class HandlerEnterSceneDoneReq extends PacketHandler {
session.send(new PacketPlayerTimeNotify(session.getPlayer())); // Probably not the right place session.send(new PacketPlayerTimeNotify(session.getPlayer())); // Probably not the right place
// Spawn player in world // Spawn player in world
session.getPlayer().getWorld().spawnPlayer(session.getPlayer()); session.getPlayer().getScene().spawnPlayer(session.getPlayer());
// Spawn other entites already in world // Spawn other entites already in world
session.getPlayer().getWorld().showOtherEntities(session.getPlayer()); session.getPlayer().getScene().showOtherEntities(session.getPlayer());
// Locations // Locations
session.send(new PacketWorldPlayerLocationNotify(session.getPlayer().getWorld())); session.send(new PacketWorldPlayerLocationNotify(session.getPlayer().getWorld()));

View File

@ -15,7 +15,7 @@ public class HandlerEntityAiSyncNotify extends PacketHandler {
EntityAiSyncNotify notify = EntityAiSyncNotify.parseFrom(payload); EntityAiSyncNotify notify = EntityAiSyncNotify.parseFrom(payload);
if (notify.getLocalAvatarAlertedMonsterListCount() > 0) { if (notify.getLocalAvatarAlertedMonsterListCount() > 0) {
session.getPlayer().getWorld().broadcastPacket(new PacketEntityAiSyncNotify(notify)); session.getPlayer().getScene().broadcastPacket(new PacketEntityAiSyncNotify(notify));
} }
} }

View File

@ -20,13 +20,13 @@ public class HandlerEvtCreateGadgetNotify extends PacketHandler {
} }
// Sanity check - dont add duplicate entities // Sanity check - dont add duplicate entities
if (session.getPlayer().getWorld().getEntityById(notify.getEntityId()) != null) { if (session.getPlayer().getScene().getEntityById(notify.getEntityId()) != null) {
return; return;
} }
// Create entity and summon in world // Create entity and summon in world
EntityClientGadget gadget = new EntityClientGadget(session.getPlayer().getWorld(), session.getPlayer(), notify); EntityClientGadget gadget = new EntityClientGadget(session.getPlayer().getScene(), session.getPlayer(), notify);
session.getPlayer().getWorld().onPlayerCreateGadget(gadget); session.getPlayer().getScene().onPlayerCreateGadget(gadget);
} }
} }

View File

@ -18,7 +18,7 @@ public class HandlerEvtDestroyGadgetNotify extends PacketHandler {
return; return;
} }
session.getPlayer().getWorld().onPlayerDestroyGadget(notify.getEntityId()); session.getPlayer().getScene().onPlayerDestroyGadget(notify.getEntityId());
} }
} }

View File

@ -1,12 +1,16 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.Grasscutter; 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.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; 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.MarkMapReqOuterClass.MarkMapReq;
import emu.grasscutter.net.proto.OperationOuterClass.Operation; import emu.grasscutter.net.proto.OperationOuterClass.Operation;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
@Opcodes(PacketOpcodes.MarkMapReq) @Opcodes(PacketOpcodes.MarkMapReq)
@ -24,9 +28,13 @@ public class HandlerMarkMapReq extends PacketHandler {
session.getPlayer().getPos().setZ(req.getMark().getPos().getZ()); session.getPlayer().getPos().setZ(req.getMark().getPos().getZ());
session.getPlayer().getPos().setY(300); 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()));
}
} }
} }

View File

@ -14,6 +14,7 @@ import emu.grasscutter.server.packet.send.PacketSceneInitFinishRsp;
import emu.grasscutter.server.packet.send.PacketScenePlayerInfoNotify; import emu.grasscutter.server.packet.send.PacketScenePlayerInfoNotify;
import emu.grasscutter.server.packet.send.PacketSceneTeamUpdateNotify; import emu.grasscutter.server.packet.send.PacketSceneTeamUpdateNotify;
import emu.grasscutter.server.packet.send.PacketSceneTimeNotify; 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.PacketServerTimeNotify;
import emu.grasscutter.server.packet.send.PacketSyncScenePlayTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketSyncScenePlayTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify;
@ -29,13 +30,14 @@ public class HandlerSceneInitFinishReq extends PacketHandler {
session.send(new PacketServerTimeNotify()); session.send(new PacketServerTimeNotify());
session.send(new PacketWorldPlayerInfoNotify(session.getPlayer().getWorld())); session.send(new PacketWorldPlayerInfoNotify(session.getPlayer().getWorld()));
session.send(new PacketWorldDataNotify(session.getPlayer().getWorld())); session.send(new PacketWorldDataNotify(session.getPlayer().getWorld()));
session.send(new PacketSceneUnlockInfoNotify());
session.send(new GenshinPacket(PacketOpcodes.SceneForceUnlockNotify)); session.send(new GenshinPacket(PacketOpcodes.SceneForceUnlockNotify));
session.send(new PacketHostPlayerNotify(session.getPlayer().getWorld())); session.send(new PacketHostPlayerNotify(session.getPlayer().getWorld()));
session.send(new PacketSceneTimeNotify(session.getPlayer())); 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 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 PacketScenePlayerInfoNotify(session.getPlayer().getWorld()));
session.send(new PacketSceneTeamUpdateNotify(session.getPlayer())); session.send(new PacketSceneTeamUpdateNotify(session.getPlayer()));

View File

@ -1,5 +1,6 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.game.World; import emu.grasscutter.game.World;
import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
@ -7,11 +8,11 @@ import emu.grasscutter.net.proto.ChangeGameTimeRspOuterClass.ChangeGameTimeRsp;
public class PacketChangeGameTimeRsp extends GenshinPacket { public class PacketChangeGameTimeRsp extends GenshinPacket {
public PacketChangeGameTimeRsp(World world) { public PacketChangeGameTimeRsp(GenshinPlayer player) {
super(PacketOpcodes.ChangeGameTimeRsp); super(PacketOpcodes.ChangeGameTimeRsp);
ChangeGameTimeRsp proto = ChangeGameTimeRsp.newBuilder() ChangeGameTimeRsp proto = ChangeGameTimeRsp.newBuilder()
.setCurGameTime(world.getTime()) .setCurGameTime(player.getScene().getTime())
.build(); .build();
this.setData(proto); this.setData(proto);

View File

@ -16,8 +16,8 @@ public class PacketGetSceneAreaRsp extends GenshinPacket {
this.buildHeader(0); this.buildHeader(0);
GetSceneAreaRsp p = GetSceneAreaRsp.newBuilder() GetSceneAreaRsp p = GetSceneAreaRsp.newBuilder()
.setSceneId(3) .setSceneId(sceneId)
.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())) .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(1).setLevel(1).build())
.addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build()) .addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build())
.addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build()) .addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build())

View File

@ -29,10 +29,6 @@ public class PacketPlayerEnterSceneNotify extends GenshinPacket {
.setWorldLevel(player.getWorldLevel()) .setWorldLevel(player.getWorldLevel())
.setEnterReason(EnterReason.Login.getValue()) .setEnterReason(EnterReason.Login.getValue())
.setIsFirstLoginEnterScene(player.isFirstLoginEnterScene()) .setIsFirstLoginEnterScene(player.isFirstLoginEnterScene())
.addSceneTagIdList(102)
.addSceneTagIdList(107)
.addSceneTagIdList(113)
.addSceneTagIdList(117)
.setUnk1(1) .setUnk1(1)
.setUnk2("3-" + player.getId() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402) .setUnk2("3-" + player.getId() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402)
.build(); .build();

View File

@ -8,11 +8,11 @@ import emu.grasscutter.net.proto.PlayerGameTimeNotifyOuterClass.PlayerGameTimeNo
public class PacketPlayerGameTimeNotify extends GenshinPacket { public class PacketPlayerGameTimeNotify extends GenshinPacket {
public PacketPlayerGameTimeNotify(World world, GenshinPlayer player) { public PacketPlayerGameTimeNotify(GenshinPlayer player) {
super(PacketOpcodes.PlayerGameTimeNotify); super(PacketOpcodes.PlayerGameTimeNotify);
PlayerGameTimeNotify proto = PlayerGameTimeNotify.newBuilder() PlayerGameTimeNotify proto = PlayerGameTimeNotify.newBuilder()
.setGameTime(world.getTime()) .setGameTime(player.getScene().getTime())
.setUid(player.getId()) .setUid(player.getId())
.build(); .build();

View File

@ -8,12 +8,12 @@ import emu.grasscutter.net.proto.SceneAreaWeatherNotifyOuterClass.SceneAreaWeath
public class PacketSceneAreaWeatherNotify extends GenshinPacket { public class PacketSceneAreaWeatherNotify extends GenshinPacket {
public PacketSceneAreaWeatherNotify(World world, GenshinPlayer player) { public PacketSceneAreaWeatherNotify(GenshinPlayer player) {
super(PacketOpcodes.SceneAreaWeatherNotify); super(PacketOpcodes.SceneAreaWeatherNotify);
SceneAreaWeatherNotify proto = SceneAreaWeatherNotify.newBuilder() SceneAreaWeatherNotify proto = SceneAreaWeatherNotify.newBuilder()
.setWeatherAreaId(1) .setWeatherAreaId(1)
.setClimateType(world.getClimate().getValue()) .setClimateType(player.getScene().getClimate().getValue())
.build(); .build();
this.setData(proto); this.setData(proto);

View File

@ -21,7 +21,7 @@ public class PacketScenePlayerInfoNotify extends GenshinPacket {
.setUid(p.getId()) .setUid(p.getId())
.setPeerId(p.getPeerId()) .setPeerId(p.getPeerId())
.setName(p.getNickname()) .setName(p.getNickname())
.setSceneId(world.getSceneId()) .setSceneId(p.getSceneId())
.setOnlinePlayerInfo(p.getOnlinePlayerInfo()) .setOnlinePlayerInfo(p.getOnlinePlayerInfo())
.build(); .build();

View File

@ -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);
}
}