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

View File

@ -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) {

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 {
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) {

View File

@ -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

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.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<GenshinPlayer> {
private final GenshinPlayer owner;
private final List<GenshinPlayer> players;
private final Int2ObjectMap<GenshinScene> scenes;
private int levelEntityId;
private int nextEntityId = 0;
private int nextPeerId = 0;
private final Int2ObjectMap<GenshinEntity> 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<GenshinPlayer> {
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<GenshinPlayer> {
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<GenshinPlayer> {
this.worldLevel = worldLevel;
}
public ClimateType getClimate() {
return climate;
}
public void setClimate(ClimateType climate) {
this.climate = climate;
}
public List<GenshinPlayer> getPlayers() {
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() {
return getPlayers().size();
}
public Int2ObjectMap<GenshinEntity> 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<GenshinPlayer> {
// 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<GenshinPlayer> {
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<GenshinPlayer> kicked = new ArrayList<>(this.getPlayers());
@ -210,11 +177,24 @@ public class World implements Iterable<GenshinPlayer> {
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<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) {
// 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<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() {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ public class HandlerEvtDestroyGadgetNotify extends PacketHandler {
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;
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()));
}
}
}

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.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()));

View File

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

View File

@ -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())

View File

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

View File

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

View File

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

View File

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

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