Copy some files from Grasscutter-Quests

NOT completely finished, nor is it completely done. Protocol issues remain! (including lack of packet IDs)
This commit is contained in:
KingRainbow44
2023-04-01 18:06:30 -04:00
parent 262ee38ded
commit daa51e53b7
381 changed files with 10285 additions and 9150 deletions

View File

@@ -1,10 +1,10 @@
package emu.grasscutter.game.world;
import java.util.List;
import lombok.Data;
@Data
public class GroupReplacementData {
int id;
List<Integer> replace_groups;
}
package emu.grasscutter.game.world;
import java.util.List;
import lombok.Data;
@Data
public class GroupReplacementData {
int id;
List<Integer> replace_groups;
}

View File

@@ -4,10 +4,16 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.binout.SceneNpcBornEntry;
import emu.grasscutter.data.binout.routes.Route;
import emu.grasscutter.data.excels.*;
import emu.grasscutter.data.excels.codex.CodexAnimalData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.data.excels.world.WorldLevelData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.dungeons.DungeonSettleListener;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.game.managers.blossom.BlossomManager;
@@ -28,6 +34,7 @@ import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -43,21 +50,25 @@ public class Scene {
@Getter private final Set<SpawnDataEntry> spawnedEntities;
@Getter private final Set<SpawnDataEntry> deadSpawnedEntities;
@Getter private final Set<SceneBlock> loadedBlocks;
@Getter private final Set<SceneGroup> loadedGroups;
@Getter private final BlossomManager blossomManager;
private final HashSet<Integer> unlockedForces;
private final List<Runnable> afterLoadedCallbacks = new ArrayList<>();
private final long startWorldTime;
@Getter @Setter DungeonManager dungeonManager;
@Getter Int2ObjectMap<Route> sceneRoutes;
private Set<SpawnDataEntry.GridBlockId> loadedGridBlocks;
@Getter @Setter private boolean dontDestroyWhenEmpty;
@Getter @Setter private int autoCloseTime;
@Getter @Setter private int time;
private final long startTime;
@Getter private final SceneScriptManager scriptManager;
@Getter @Setter private WorldChallenge challenge;
@Getter private List<DungeonSettleListener> dungeonSettleListeners;
@Getter private DungeonData dungeonData;
@Getter @Setter private int prevScene; // Id of the previous scene
@Getter @Setter private int prevScenePoint;
@Getter @Setter private int killedMonsterCount;
private Set<SceneNpcBornEntry> npcBornEntrySet;
@Getter private boolean finishedLoading = false;
@Getter private int tickCount = 0;
@Getter private boolean isPaused = false;
public Scene(World world, SceneData sceneData) {
this.world = world;
@@ -65,17 +76,20 @@ public class Scene {
this.players = new CopyOnWriteArrayList<>();
this.entities = new ConcurrentHashMap<>();
this.time = 8 * 60;
this.startTime = System.currentTimeMillis();
this.prevScene = 3;
this.sceneRoutes = GameData.getSceneRoutes(getId());
this.startWorldTime = world.getWorldTime();
this.spawnedEntities = ConcurrentHashMap.newKeySet();
this.deadSpawnedEntities = ConcurrentHashMap.newKeySet();
this.loadedBlocks = ConcurrentHashMap.newKeySet();
this.loadedGroups = ConcurrentHashMap.newKeySet();
this.loadedGridBlocks = new HashSet<>();
this.npcBornEntrySet = ConcurrentHashMap.newKeySet();
this.scriptManager = new SceneScriptManager(this);
this.blossomManager = new BlossomManager(this);
this.unlockedForces = new HashSet<>();
}
public int getId() {
@@ -101,31 +115,55 @@ public class Scene {
.orElse(null);
}
public void changeTime(int time) {
this.time = time % 1440;
}
public int getSceneTime() {
return (int) (System.currentTimeMillis() - this.startTime);
}
public void setDungeonData(DungeonData dungeonData) {
if (dungeonData == null
|| this.dungeonData != null
|| this.getSceneType() != SceneType.SCENE_DUNGEON
|| dungeonData.getSceneId() != this.getId()) {
return;
/**
* Sets the scene's pause state. Sends the current scene's time to all players.
*
* @param paused The new pause state.
*/
public void setPaused(boolean paused) {
if (this.isPaused != paused) {
this.isPaused = paused;
this.broadcastPacket(new PacketSceneTimeNotify(this));
}
this.dungeonData = dungeonData;
}
/**
* Gets the time in seconds since the scene started.
*
* @return The time in seconds since the scene started.
*/
public int getSceneTime() {
return (int) (this.getWorld().getWorldTime() - this.startWorldTime);
}
/**
* Gets {@link Scene#getSceneTime()} in seconds.
*
* @return The time in seconds since the scene started.
*/
public int getSceneTimeSeconds() {
return this.getSceneTime() / 1000;
}
public void addDungeonSettleObserver(DungeonSettleListener dungeonSettleListener) {
if (dungeonSettleListeners == null) {
dungeonSettleListeners = new ArrayList<>();
}
dungeonSettleListeners.add(dungeonSettleListener);
}
/**
* Triggers an event in the dungeon manager.
*
* @param conditionType The condition type to trigger.
* @param params The parameters to pass to the event.
*/
public void triggerDungeonEvent(DungeonPassConditionType conditionType, int... params) {
if (this.dungeonManager == null) return;
this.dungeonManager.triggerEvent(conditionType, params);
}
public boolean isInScene(GameEntity entity) {
return this.entities.containsKey(entity.getId());
}

View File

@@ -1,86 +1,85 @@
package emu.grasscutter.game.world;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup;
import lombok.Getter;
import lombok.Setter;
@Entity(value = "group_instances", useDiscriminator = false)
public class SceneGroupInstance {
@Id private ObjectId id;
@Indexed private int ownerUid; //This group is owned by the host player
@Getter private int groupId;
@Getter private transient SceneGroup luaGroup;
@Getter @Setter private int targetSuiteId;
@Getter @Setter private int activeSuiteId;
@Getter private Set<Integer> deadEntities; //Config_ids
private boolean isCached;
@Getter private Map<Integer, Integer> cachedGadgetStates;
@Getter private Map<String, Integer> cachedVariables;
@Getter @Setter private int lastTimeRefreshed;
public SceneGroupInstance(SceneGroup group, Player owner) {
this.luaGroup = group;
this.groupId = group.id;
this.targetSuiteId = 0;
this.activeSuiteId = 0;
this.lastTimeRefreshed = 0;
this.ownerUid = owner.getUid();
this.deadEntities = new HashSet<>();
this.cachedGadgetStates = new ConcurrentHashMap<>();
this.cachedVariables = new ConcurrentHashMap<>();
this.isCached = false; //This is true when the group is not loaded on scene but caches suite data
}
@Deprecated // Morphia only!
SceneGroupInstance(){
this.cachedVariables = new ConcurrentHashMap<>();
this.deadEntities = new HashSet<>();
this.cachedGadgetStates = new ConcurrentHashMap<>();
}
public void setLuaGroup(SceneGroup group) {
this.luaGroup = group;
this.groupId = group.id;
}
public boolean isCached() {
return this.isCached;
}
public void setCached(boolean value) {
this.isCached = value;
save(); //Save each time a group is registered or unregistered
}
public void cacheGadgetState(SceneGadget g, int state) {
if(g.persistent) //Only cache when is persistent
cachedGadgetStates.put(g.config_id, state);
}
public int getCachedGadgetState(SceneGadget g) {
Integer state = cachedGadgetStates.getOrDefault(g.config_id, null);
return (state == null) ? g.state : state;
}
public void save() {
DatabaseHelper.saveGroupInstance(this);
}
}
package emu.grasscutter.game.world;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.Getter;
import lombok.Setter;
import org.bson.types.ObjectId;
@Entity(value = "group_instances", useDiscriminator = false)
public class SceneGroupInstance {
@Id private ObjectId id;
@Indexed private int ownerUid; // This group is owned by the host player
@Getter private int groupId;
@Getter private transient SceneGroup luaGroup;
@Getter @Setter private int targetSuiteId;
@Getter @Setter private int activeSuiteId;
@Getter private Set<Integer> deadEntities; // Config_ids
private boolean isCached;
@Getter private Map<Integer, Integer> cachedGadgetStates;
@Getter private Map<String, Integer> cachedVariables;
@Getter @Setter private int lastTimeRefreshed;
public SceneGroupInstance(SceneGroup group, Player owner) {
this.luaGroup = group;
this.groupId = group.id;
this.targetSuiteId = 0;
this.activeSuiteId = 0;
this.lastTimeRefreshed = 0;
this.ownerUid = owner.getUid();
this.deadEntities = new HashSet<>();
this.cachedGadgetStates = new ConcurrentHashMap<>();
this.cachedVariables = new ConcurrentHashMap<>();
this.isCached =
false; // This is true when the group is not loaded on scene but caches suite data
}
@Deprecated // Morphia only!
SceneGroupInstance() {
this.cachedVariables = new ConcurrentHashMap<>();
this.deadEntities = new HashSet<>();
this.cachedGadgetStates = new ConcurrentHashMap<>();
}
public void setLuaGroup(SceneGroup group) {
this.luaGroup = group;
this.groupId = group.id;
}
public boolean isCached() {
return this.isCached;
}
public void setCached(boolean value) {
this.isCached = value;
save(); // Save each time a group is registered or unregistered
}
public void cacheGadgetState(SceneGadget g, int state) {
if (g.persistent) // Only cache when is persistent
cachedGadgetStates.put(g.config_id, state);
}
public int getCachedGadgetState(SceneGadget g) {
Integer state = cachedGadgetStates.getOrDefault(g.config_id, null);
return (state == null) ? g.state : state;
}
public void save() {
DatabaseHelper.saveGroupInstance(this);
}
}

View File

@@ -1,13 +1,16 @@
package emu.grasscutter.game.world;
import static emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType.SCRIPT;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.data.excels.SceneData;
import emu.grasscutter.data.excels.dungeon.DungeonData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.Player.SceneLoadState;
import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.data.TeleportProperties;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.scripts.data.SceneConfig;
@@ -15,6 +18,7 @@ import emu.grasscutter.server.event.player.PlayerTeleportEvent;
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.ConversionUtils;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
@@ -24,26 +28,33 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.val;
public class World implements Iterable<Player> {
private final GameServer server;
private final Player owner;
private final List<Player> players;
private final Int2ObjectMap<Scene> scenes;
@Getter private final GameServer server;
@Getter private final Player host;
@Getter private final List<Player> players;
@Getter private final Int2ObjectMap<Scene> scenes;
private final int levelEntityId;
@Getter private final int levelEntityId;
private int nextEntityId = 0;
private int nextPeerId = 0;
private int worldLevel;
private final boolean isMultiplayer;
private long lastUpdateTime;
@Getter private int tickCount = 0;
@Getter private boolean isPaused = false;
@Getter private long currentWorldTime = 0;
public World(Player player) {
this(player, false);
}
public World(Player player, boolean isMultiplayer) {
this.owner = player;
this.host = player;
this.server = player.getServer();
this.players = Collections.synchronizedList(new ArrayList<>());
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
@@ -52,26 +63,19 @@ public class World implements Iterable<Player> {
this.worldLevel = player.getWorldLevel();
this.isMultiplayer = isMultiplayer;
this.owner.getServer().registerWorld(this);
}
public Player getHost() {
return owner;
}
public GameServer getServer() {
return server;
}
public int getLevelEntityId() {
return levelEntityId;
this.lastUpdateTime = System.currentTimeMillis();
this.currentWorldTime = host.getPlayerGameTime();
this.host.getServer().registerWorld(this);
}
/**
* Gets the peer ID of the world's host.
*
* @return The peer ID of the world's host. 0 if the host is null.
*/
public int getHostPeerId() {
if (this.getHost() == null) {
return 0;
}
return this.getHost().getPeerId();
return this.getHost() == null ? 0 : this.getHost().getPeerId();
}
public int getNextPeerId() {
@@ -86,23 +90,21 @@ public class World implements Iterable<Player> {
this.worldLevel = worldLevel;
}
public List<Player> getPlayers() {
return players;
}
public Int2ObjectMap<Scene> getScenes() {
return this.scenes;
}
/**
* Gets an associated scene by ID. Creates a new instance of the scene if it doesn't exist.
*
* @param sceneId The scene ID.
* @return The scene.
*/
public Scene getSceneById(int sceneId) {
// Get scene normally
Scene scene = this.getScenes().get(sceneId);
var scene = this.getScenes().get(sceneId);
if (scene != null) {
return scene;
}
// Create scene from scene data if it doesnt exist
SceneData sceneData = GameData.getSceneDataMap().get(sceneId);
// Create scene from scene data if it doesn't exist
var sceneData = GameData.getSceneDataMap().get(sceneId);
if (sceneData != null) {
scene = new Scene(this, sceneData);
this.registerScene(scene);
@@ -120,6 +122,12 @@ public class World implements Iterable<Player> {
return isMultiplayer;
}
/**
* Gets the next entity ID for the specified entity type.
*
* @param idType The entity type.
* @return The next entity ID.
*/
public int getNextEntityId(EntityIdType idType) {
return (idType.getId() << 24) + ++this.nextEntityId;
}
@@ -231,80 +239,112 @@ public class World implements Iterable<Player> {
TeleportType teleportType,
DungeonData dungeonData,
Position teleportTo) {
EnterReason enterReason =
switch (teleportType) {
// shouldn't affect the teleportation, but its clearer when inspecting the packets
// TODO add more conditions for different reason.
case INTERNAL -> EnterReason.TransPoint;
case WAYPOINT -> EnterReason.TransPoint;
case MAP -> EnterReason.TransPoint;
case COMMAND -> EnterReason.Gm;
case SCRIPT -> EnterReason.Lua;
case CLIENT -> EnterReason.ClientTransmit;
case DUNGEON -> EnterReason.DungeonEnter;
default -> EnterReason.None;
};
return transferPlayerToScene(
player, sceneId, teleportType, enterReason, dungeonData, teleportTo);
}
public boolean transferPlayerToScene(
Player player,
int sceneId,
TeleportType teleportType,
EnterReason enterReason,
DungeonData dungeonData,
Position teleportTo) {
// Get enter types
val teleportProps =
TeleportProperties.builder()
.sceneId(sceneId)
.teleportType(teleportType)
.enterReason(enterReason)
.teleportTo(teleportTo)
.enterType(EnterType.ENTER_TYPE_JUMP);
val sceneData = GameData.getSceneDataMap().get(sceneId);
if (dungeonData != null) {
teleportProps.enterType(EnterType.ENTER_TYPE_DUNGEON).enterReason(EnterReason.DungeonEnter);
} else if (player.getSceneId() == sceneId) {
teleportProps.enterType(EnterType.ENTER_TYPE_GOTO);
} else if (sceneData != null && sceneData.getSceneType() == SceneType.SCENE_HOME_WORLD) {
// Home
teleportProps.enterType(EnterType.ENTER_TYPE_SELF_HOME).enterReason(EnterReason.EnterHome);
}
return transferPlayerToScene(player, teleportProps.build());
}
public boolean transferPlayerToScene(Player player, TeleportProperties teleportProperties) {
// Call player teleport event.
PlayerTeleportEvent event =
new PlayerTeleportEvent(player, teleportType, player.getPosition(), teleportTo);
new PlayerTeleportEvent(player, teleportProperties, player.getPosition());
// Call event & check if it was canceled.
event.call();
if (event.isCanceled()) {
return false; // Teleport was canceled.
}
// Set the destination.
teleportTo = event.getDestination();
if (GameData.getSceneDataMap().get(sceneId) == null) {
if (GameData.getSceneDataMap().get(teleportProperties.getSceneId()) == null) {
return false;
}
Scene oldScene = null;
if (player.getScene() != null) {
oldScene = player.getScene();
// Don't deregister scenes if the player is going to tp back into them
if (oldScene.getId() == sceneId) {
if (oldScene.getId() == teleportProperties.getSceneId()) {
oldScene.setDontDestroyWhenEmpty(true);
}
oldScene.removePlayer(player);
}
Scene newScene = this.getSceneById(sceneId);
newScene.setDungeonData(dungeonData);
var newScene = this.getSceneById(teleportProperties.getSceneId());
newScene.addPlayer(player);
player.getTeamManager().applyAbilities(newScene);
// Dungeon
SceneConfig config = newScene.getScriptManager().getConfig();
if (teleportTo == null && config != null) {
if (teleportProperties.getTeleportTo() == null && config != null) {
if (config.born_pos != null) {
teleportTo = newScene.getScriptManager().getConfig().born_pos;
teleportProperties.setTeleportTo(newScene.getScriptManager().getConfig().born_pos);
}
if (config.born_rot != null) {
player.getRotation().set(config.born_rot);
teleportProperties.setTeleportRot(config.born_rot);
}
}
// Set player position
if (teleportTo == null) {
teleportTo = player.getPosition();
// Set player position and rotation
if (teleportProperties.getTeleportTo() != null) {
player.getPosition().set(teleportProperties.getTeleportTo());
}
if (teleportProperties.getTeleportRot() != null) {
player.getRotation().set(teleportProperties.getTeleportRot());
}
player.getPosition().set(teleportTo);
if (oldScene != null) {
if (oldScene != null && newScene != oldScene) {
newScene.setPrevScene(oldScene.getId());
oldScene.setDontDestroyWhenEmpty(false);
}
// Get enter types
EnterType enterType = EnterType.ENTER_TYPE_JUMP;
EnterReason enterReason = EnterReason.TransPoint;
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, teleportProperties));
if (dungeonData != null) {
enterType = EnterType.ENTER_TYPE_DUNGEON;
enterReason = EnterReason.DungeonEnter;
} else if (oldScene == newScene) {
enterType = EnterType.ENTER_TYPE_GOTO;
} else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) {
// Home
enterReason = EnterReason.EnterHome;
enterType = EnterType.ENTER_TYPE_SELF_HOME;
if (teleportProperties.getTeleportType() != TeleportType.INTERNAL
&& teleportProperties.getTeleportType() != SCRIPT) {
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_ANY_MANUAL_TRANSPORT);
}
// Teleport packet
player.sendPacket(
new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, teleportTo));
return true;
}
@@ -347,15 +387,114 @@ public class World implements Iterable<Player> {
}
}
// Returns true if the world should be deleted
/**
* Invoked every game tick.
*
* @return True if the world should be removed.
*/
public boolean onTick() {
// Check if there are players in this world.
if (this.getPlayerCount() == 0) return true;
this.scenes.forEach((k, scene) -> scene.onTick());
// Tick all associated scenes.
this.getScenes().forEach((k, scene) -> scene.onTick());
// sync time every 10 seconds
if (this.tickCount % 10 == 0) {
this.getPlayers().forEach(p -> p.sendPacket(new PacketPlayerGameTimeNotify(p)));
}
// store updated world time every 60 seconds. (in-game hour)
if (this.tickCount % 60 == 0) {
this.getHost().updatePlayerGameTime(currentWorldTime);
}
this.tickCount++;
return false;
}
public void close() {}
/** Returns the in-game world time in real milliseconds. */
public long getWorldTime() {
if (!this.isPaused) {
var newUpdateTime = System.currentTimeMillis();
this.currentWorldTime += (newUpdateTime - lastUpdateTime);
this.lastUpdateTime = newUpdateTime;
}
return currentWorldTime;
}
/** Returns the current in game days world time in ingame minutes (0-1439) */
public int getGameTime() {
return (int) (getTotalGameTimeMinutes() % 1440);
}
/** Returns the current in game days world time in ingame hours (0-23) */
public int getGameTimeHours() {
return this.getGameTime() / 60;
}
/** Returns the total number of in game days that got completed since the beginning of the game */
public long getTotalGameTimeDays() {
return ConversionUtils.gameTimeToDays(getTotalGameTimeMinutes());
}
/**
* Returns the total number of in game hours that got completed since the beginning of the game
*/
public long getTotalGameTimeHours() {
return ConversionUtils.gameTimeToHours(getTotalGameTimeMinutes());
}
/** Returns the elapsed in-game minutes since the creation of the world. */
public long getTotalGameTimeMinutes() {
return this.getWorldTime() / 1000;
}
/**
* Sets the world's pause status. Updates players and scenes accordingly.
*
* @param paused True if the world should be paused.
*/
public void setPaused(boolean paused) {
this.getWorldTime(); // Update the world time.
// If the world is being un-paused, update the last update time.
if (this.isPaused != paused && !paused) {
this.lastUpdateTime = System.currentTimeMillis();
}
this.isPaused = paused;
this.getPlayers().forEach(player -> player.setPaused(paused));
this.getScenes().forEach((key, scene) -> scene.setPaused(paused));
}
/**
* Changes the time of the world.
*
* @param time The new time in minutes.
* @param days The number of days to add.
*/
public void changeTime(int time, int days) {
// Calculate time differences.
var currentTime = this.getGameTime();
var diff = time - currentTime;
if (diff < 0) diff = 1440 + diff;
// Update the world time.
this.currentWorldTime += days * 1440 * 1000L + diff * 1000L;
// Update all players.
this.host.updatePlayerGameTime(currentWorldTime);
this.players.forEach(
player ->
player
.getQuestManager()
.queueEvent(
QuestContent.QUEST_CONTENT_GAME_TIME_TICK, this.getGameTimeHours(), days));
}
@Override
public Iterator<Player> iterator() {
return this.getPlayers().iterator();

View File

@@ -5,7 +5,7 @@ import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.InvestigationMonsterData;
import emu.grasscutter.data.excels.RewardPreviewData;
import emu.grasscutter.data.excels.WorldLevelData;
import emu.grasscutter.data.excels.world.WorldLevelData;
import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler;
import emu.grasscutter.game.entity.gadget.chest.ChestInteractHandler;
import emu.grasscutter.game.entity.gadget.chest.NormalChestInteractHandler;

View File

@@ -1,19 +1,19 @@
package emu.grasscutter.game.world.data;
import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.net.proto.EnterTypeOuterClass;
import emu.grasscutter.server.event.player.PlayerTeleportEvent;
import emu.grasscutter.utils.Position;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TeleportProperties {
private final int sceneId;
private final PlayerTeleportEvent.TeleportType teleportType;
private final EnterReason enterReason;
private Position teleportTo;
private Position teleportRot;
private EnterTypeOuterClass.EnterType enterType;
}
package emu.grasscutter.game.world.data;
import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.net.proto.EnterTypeOuterClass;
import emu.grasscutter.server.event.player.PlayerTeleportEvent;
import emu.grasscutter.utils.Position;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TeleportProperties {
private final int sceneId;
private final PlayerTeleportEvent.TeleportType teleportType;
private final EnterReason enterReason;
private Position teleportTo;
private Position teleportRot;
private EnterTypeOuterClass.EnterType enterType;
}