Commit ae2d1fe4 authored by github-actions's avatar github-actions Committed by Melledy
Browse files

Fix whitespace [skip actions]

parent 510d564b
......@@ -16,7 +16,7 @@ import java.util.ArrayList;
import java.util.List;
public class TowerSystem extends BaseGameSystem {
public TowerSystem(GameServer server) {
super(server);
this.load();
......@@ -24,7 +24,7 @@ public class TowerSystem extends BaseGameSystem {
private TowerScheduleConfig towerScheduleConfig;
public synchronized void load(){
public synchronized void load() {
try (Reader fileReader = DataLoader.loadReader("TowerSchedule.json")) {
towerScheduleConfig = Grasscutter.getGsonFactory().fromJson(fileReader, TowerScheduleConfig.class);
} catch (Exception e) {
......@@ -36,13 +36,13 @@ public class TowerSystem extends BaseGameSystem {
return towerScheduleConfig;
}
public TowerScheduleData getCurrentTowerScheduleData(){
public TowerScheduleData getCurrentTowerScheduleData() {
var data = GameData.getTowerScheduleDataMap().get(towerScheduleConfig.getScheduleId());
if(data == null){
if (data == null) {
Grasscutter.getLogger().error("Could not get current tower schedule data by schedule id {}, please check your resource files",
towerScheduleConfig.getScheduleId());
}
return data;
}
......@@ -56,29 +56,29 @@ public class TowerSystem extends BaseGameSystem {
return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList();
}
public int getNextFloorId(int floorId){
public int getNextFloorId(int floorId) {
var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId();
var scheduleFloors = getScheduleFloors();
var nextId = 0;
// find in entrance floors first
for(int i=0;i<entranceFloors.size()-1;i++){
if(floorId == entranceFloors.get(i)){
for (int i=0;i<entranceFloors.size()-1;i++) {
if (floorId == entranceFloors.get(i)) {
nextId = entranceFloors.get(i+1);
}
}
if(floorId == entranceFloors.get(entranceFloors.size()-1)){
if (floorId == entranceFloors.get(entranceFloors.size()-1)) {
nextId = scheduleFloors.get(0);
}
if(nextId != 0){
if (nextId != 0) {
return nextId;
}
// find in schedule floors
for(int i=0; i < scheduleFloors.size() - 1; i++){
if(floorId == scheduleFloors.get(i)){
for (int i=0; i < scheduleFloors.size() - 1; i++) {
if (floorId == scheduleFloors.get(i)) {
nextId = scheduleFloors.get(i + 1);
}
}return nextId;
......
......@@ -30,423 +30,423 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class Scene {
private final World world;
private final SceneData sceneData;
private final List<Player> players;
private final Map<Integer, GameEntity> entities;
private final Set<SpawnDataEntry> spawnedEntities;
private final Set<SpawnDataEntry> deadSpawnedEntities;
private final Set<SceneBlock> loadedBlocks;
private Set<SpawnDataEntry.GridBlockId> loadedGridBlocks;
private boolean dontDestroyWhenEmpty;
private int autoCloseTime;
private int time;
private SceneScriptManager scriptManager;
private WorldChallenge challenge;
private List<DungeonSettleListener> dungeonSettleListeners;
private DungeonData dungeonData;
private int prevScene; // Id of the previous scene
private int prevScenePoint;
private Set<SceneNpcBornEntry> npcBornEntrySet;
public Scene(World world, SceneData sceneData) {
this.world = world;
this.sceneData = sceneData;
this.players = new CopyOnWriteArrayList<>();
this.entities = new ConcurrentHashMap<>();
this.time = 8 * 60;
this.prevScene = 3;
this.spawnedEntities = ConcurrentHashMap.newKeySet();
this.deadSpawnedEntities = ConcurrentHashMap.newKeySet();
this.loadedBlocks = ConcurrentHashMap.newKeySet();
this.loadedGridBlocks = new HashSet<>();
this.npcBornEntrySet = ConcurrentHashMap.newKeySet();
this.scriptManager = new SceneScriptManager(this);
}
public int getId() {
return sceneData.getId();
}
public World getWorld() {
return world;
}
public SceneData getSceneData() {
return this.sceneData;
}
public SceneType getSceneType() {
return getSceneData().getSceneType();
}
public List<Player> getPlayers() {
return players;
}
public int getPlayerCount() {
return this.getPlayers().size();
}
public Map<Integer, GameEntity> getEntities() {
return entities;
}
public GameEntity getEntityById(int id) {
return this.entities.get(id);
}
public GameEntity getEntityByConfigId(int configId) {
return this.entities.values().stream()
.filter(x -> x.getConfigId() == configId)
.findFirst()
.orElse(null);
}
/**
* @return the autoCloseTime
*/
public int getAutoCloseTime() {
return autoCloseTime;
}
/**
* @param autoCloseTime the autoCloseTime to set
*/
public void setAutoCloseTime(int autoCloseTime) {
this.autoCloseTime = autoCloseTime;
}
public int getTime() {
return time;
}
public void changeTime(int time) {
this.time = time % 1440;
}
public int getPrevScene() {
return prevScene;
}
public void setPrevScene(int prevScene) {
this.prevScene = prevScene;
}
public int getPrevScenePoint() {
return prevScenePoint;
}
public void setPrevScenePoint(int prevPoint) {
this.prevScenePoint = prevPoint;
}
public boolean dontDestroyWhenEmpty() {
return dontDestroyWhenEmpty;
}
public void setDontDestroyWhenEmpty(boolean dontDestroyWhenEmpty) {
this.dontDestroyWhenEmpty = dontDestroyWhenEmpty;
}
public Set<SceneBlock> getLoadedBlocks() {
return loadedBlocks;
}
public Set<SpawnDataEntry> getSpawnedEntities() {
return spawnedEntities;
}
public Set<SpawnDataEntry> getDeadSpawnedEntities() {
return deadSpawnedEntities;
}
public SceneScriptManager getScriptManager() {
return scriptManager;
}
public DungeonData getDungeonData() {
return dungeonData;
}
public void setDungeonData(DungeonData dungeonData) {
if (dungeonData == null || this.dungeonData != null || this.getSceneType() != SceneType.SCENE_DUNGEON || dungeonData.getSceneId() != this.getId()) {
return;
}
this.dungeonData = dungeonData;
}
public WorldChallenge getChallenge() {
return challenge;
}
public void setChallenge(WorldChallenge challenge) {
this.challenge = challenge;
}
public void addDungeonSettleObserver(DungeonSettleListener dungeonSettleListener){
if(dungeonSettleListeners == null){
dungeonSettleListeners = new ArrayList<>();
}
dungeonSettleListeners.add(dungeonSettleListener);
}
public List<DungeonSettleListener> getDungeonSettleObservers() {
return dungeonSettleListeners;
}
public boolean isInScene(GameEntity entity) {
return this.entities.containsKey(entity.getId());
}
public synchronized void addPlayer(Player 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 synchronized void removePlayer(Player player) {
// Remove from challenge if leaving
if (this.getChallenge() != null && this.getChallenge().inProgress()) {
player.sendPacket(new PacketDungeonChallengeFinishNotify(this.getChallenge()));
}
// Remove player from scene
getPlayers().remove(player);
player.setScene(null);
// Remove player avatars
this.removePlayerAvatars(player);
// Remove player gadgets
for (EntityBaseGadget gadget : player.getTeamManager().getGadgets()) {
this.removeEntity(gadget);
}
// Deregister scene if not in use
if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty()) {
this.getWorld().deregisterScene(this);
}
}
private void setupPlayerAvatars(Player 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(Player player) {
Iterator<EntityAvatar> it = player.getTeamManager().getActiveTeam().iterator();
while (it.hasNext()) {
this.removeEntity(it.next(), VisionType.VISION_TYPE_REMOVE);
it.remove();
}
}
public void spawnPlayer(Player 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());
// Notify the client of any extra skill charges
for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) {
if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) {
player.sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar()));
}
}
}
private void addEntityDirectly(GameEntity entity) {
getEntities().put(entity.getId(), entity);
entity.onCreate(); // Call entity create event
}
public synchronized void addEntity(GameEntity entity) {
this.addEntityDirectly(entity);
this.broadcastPacket(new PacketSceneEntityAppearNotify(entity));
}
public synchronized void addEntityToSingleClient(Player player, GameEntity entity) {
this.addEntityDirectly(entity);
player.sendPacket(new PacketSceneEntityAppearNotify(entity));
}
public void addEntities(Collection<? extends GameEntity> entities){
addEntities(entities, VisionType.VISION_TYPE_BORN);
}
public synchronized void addEntities(Collection<? extends GameEntity> entities, VisionType visionType) {
if(entities == null || entities.isEmpty()){
return;
}
for (GameEntity entity : entities) {
this.addEntityDirectly(entity);
}
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, visionType));
}
private GameEntity removeEntityDirectly(GameEntity entity) {
return getEntities().remove(entity.getId());
}
public void removeEntity(GameEntity entity) {
this.removeEntity(entity, VisionType.VISION_TYPE_DIE);
}
public synchronized void removeEntity(GameEntity entity, VisionType visionType) {
GameEntity removed = this.removeEntityDirectly(entity);
if (removed != null) {
this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType));
}
}
public synchronized void removeEntities(List<GameEntity> entity, VisionType visionType) {
var toRemove = entity.stream()
.map(this::removeEntityDirectly)
.toList();
if (toRemove.size() > 0) {
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType));
}
}
public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) {
this.removeEntityDirectly(oldEntity);
this.addEntityDirectly(newEntity);
this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VISION_TYPE_REPLACE));
this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VISION_TYPE_REPLACE, oldEntity.getId()));
}
public void showOtherEntities(Player player) {
List<GameEntity> entities = new LinkedList<>();
GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity();
for (GameEntity entity : this.getEntities().values()) {
if (entity == currentEntity) {
continue;
}
entities.add(entity);
}
player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET));
}
public void handleAttack(AttackResult result) {
//GameEntity attacker = getEntityById(result.getAttackerId());
GameEntity target = getEntityById(result.getDefenseId());
if (target == null) {
return;
}
// Godmode check
if (target instanceof EntityAvatar) {
if (((EntityAvatar) target).getPlayer().inGodmode()) {
return;
}
}
// Sanity check
target.damage(result.getDamage(), result.getAttackerId());
}
public void killEntity(GameEntity target) {
killEntity(target, 0);
}
public void killEntity(GameEntity target, int attackerId) {
GameEntity attacker = null;
if (attackerId > 0) {
attacker = getEntityById(attackerId);
}
if (attacker != null) {
// Check codex
if (attacker instanceof EntityClientGadget gadgetAttacker) {
var clientGadgetOwner = getEntityById(gadgetAttacker.getOwnerEntityId());
if (clientGadgetOwner instanceof EntityAvatar) {
((EntityClientGadget) attacker).getOwner().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL);
}
} else if (attacker instanceof EntityAvatar avatarAttacker) {
avatarAttacker.getPlayer().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL);
}
}
// Packet
this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD));
// Reward drop
if (target instanceof EntityMonster && this.getSceneType() != SceneType.SCENE_DUNGEON) {
getWorld().getServer().getDropSystem().callDrop((EntityMonster) target);
}
// Remove entity from world
this.removeEntity(target);
// Death event
target.onDeath(attackerId);
}
public void onTick() {
// disable script for home
if (this.getSceneType() == SceneType.SCENE_HOME_WORLD || this.getSceneType() == SceneType.SCENE_HOME_ROOM){
return;
}
if (this.getScriptManager().isInit()) {
this.checkBlocks();
} else {
// TEMPORARY
this.checkSpawns();
}
// Triggers
this.scriptManager.checkRegions();
if(challenge != null){
challenge.onCheckTimeOut();
}
private final World world;
private final SceneData sceneData;
private final List<Player> players;
private final Map<Integer, GameEntity> entities;
private final Set<SpawnDataEntry> spawnedEntities;
private final Set<SpawnDataEntry> deadSpawnedEntities;
private final Set<SceneBlock> loadedBlocks;
private Set<SpawnDataEntry.GridBlockId> loadedGridBlocks;
private boolean dontDestroyWhenEmpty;
private int autoCloseTime;
private int time;
private SceneScriptManager scriptManager;
private WorldChallenge challenge;
private List<DungeonSettleListener> dungeonSettleListeners;
private DungeonData dungeonData;
private int prevScene; // Id of the previous scene
private int prevScenePoint;
private Set<SceneNpcBornEntry> npcBornEntrySet;
public Scene(World world, SceneData sceneData) {
this.world = world;
this.sceneData = sceneData;
this.players = new CopyOnWriteArrayList<>();
this.entities = new ConcurrentHashMap<>();
this.time = 8 * 60;
this.prevScene = 3;
this.spawnedEntities = ConcurrentHashMap.newKeySet();
this.deadSpawnedEntities = ConcurrentHashMap.newKeySet();
this.loadedBlocks = ConcurrentHashMap.newKeySet();
this.loadedGridBlocks = new HashSet<>();
this.npcBornEntrySet = ConcurrentHashMap.newKeySet();
this.scriptManager = new SceneScriptManager(this);
}
public int getId() {
return sceneData.getId();
}
public World getWorld() {
return world;
}
public SceneData getSceneData() {
return this.sceneData;
}
public SceneType getSceneType() {
return getSceneData().getSceneType();
}
public List<Player> getPlayers() {
return players;
}
public int getPlayerCount() {
return this.getPlayers().size();
}
public Map<Integer, GameEntity> getEntities() {
return entities;
}
public GameEntity getEntityById(int id) {
return this.entities.get(id);
}
public GameEntity getEntityByConfigId(int configId) {
return this.entities.values().stream()
.filter(x -> x.getConfigId() == configId)
.findFirst()
.orElse(null);
}
/**
* @return the autoCloseTime
*/
public int getAutoCloseTime() {
return autoCloseTime;
}
/**
* @param autoCloseTime the autoCloseTime to set
*/
public void setAutoCloseTime(int autoCloseTime) {
this.autoCloseTime = autoCloseTime;
}
public int getTime() {
return time;
}
public void changeTime(int time) {
this.time = time % 1440;
}
public int getPrevScene() {
return prevScene;
}
public void setPrevScene(int prevScene) {
this.prevScene = prevScene;
}
public int getPrevScenePoint() {
return prevScenePoint;
}
public void setPrevScenePoint(int prevPoint) {
this.prevScenePoint = prevPoint;
}
public boolean dontDestroyWhenEmpty() {
return dontDestroyWhenEmpty;
}
public void setDontDestroyWhenEmpty(boolean dontDestroyWhenEmpty) {
this.dontDestroyWhenEmpty = dontDestroyWhenEmpty;
}
public Set<SceneBlock> getLoadedBlocks() {
return loadedBlocks;
}
public Set<SpawnDataEntry> getSpawnedEntities() {
return spawnedEntities;
}
public Set<SpawnDataEntry> getDeadSpawnedEntities() {
return deadSpawnedEntities;
}
public SceneScriptManager getScriptManager() {
return scriptManager;
}
public DungeonData getDungeonData() {
return dungeonData;
}
public void setDungeonData(DungeonData dungeonData) {
if (dungeonData == null || this.dungeonData != null || this.getSceneType() != SceneType.SCENE_DUNGEON || dungeonData.getSceneId() != this.getId()) {
return;
}
this.dungeonData = dungeonData;
}
public WorldChallenge getChallenge() {
return challenge;
}
public void setChallenge(WorldChallenge challenge) {
this.challenge = challenge;
}
public void addDungeonSettleObserver(DungeonSettleListener dungeonSettleListener) {
if (dungeonSettleListeners == null) {
dungeonSettleListeners = new ArrayList<>();
}
dungeonSettleListeners.add(dungeonSettleListener);
}
public List<DungeonSettleListener> getDungeonSettleObservers() {
return dungeonSettleListeners;
}
public boolean isInScene(GameEntity entity) {
return this.entities.containsKey(entity.getId());
}
public synchronized void addPlayer(Player 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 synchronized void removePlayer(Player player) {
// Remove from challenge if leaving
if (this.getChallenge() != null && this.getChallenge().inProgress()) {
player.sendPacket(new PacketDungeonChallengeFinishNotify(this.getChallenge()));
}
// Remove player from scene
getPlayers().remove(player);
player.setScene(null);
// Remove player avatars
this.removePlayerAvatars(player);
// Remove player gadgets
for (EntityBaseGadget gadget : player.getTeamManager().getGadgets()) {
this.removeEntity(gadget);
}
// Deregister scene if not in use
if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty()) {
this.getWorld().deregisterScene(this);
}
}
private void setupPlayerAvatars(Player 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(Player player) {
Iterator<EntityAvatar> it = player.getTeamManager().getActiveTeam().iterator();
while (it.hasNext()) {
this.removeEntity(it.next(), VisionType.VISION_TYPE_REMOVE);
it.remove();
}
}
public void spawnPlayer(Player 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());
// Notify the client of any extra skill charges
for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) {
if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) {
player.sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar()));
}
}
}
private void addEntityDirectly(GameEntity entity) {
getEntities().put(entity.getId(), entity);
entity.onCreate(); // Call entity create event
}
public synchronized void addEntity(GameEntity entity) {
this.addEntityDirectly(entity);
this.broadcastPacket(new PacketSceneEntityAppearNotify(entity));
}
public synchronized void addEntityToSingleClient(Player player, GameEntity entity) {
this.addEntityDirectly(entity);
player.sendPacket(new PacketSceneEntityAppearNotify(entity));
}
public void addEntities(Collection<? extends GameEntity> entities) {
addEntities(entities, VisionType.VISION_TYPE_BORN);
}
public synchronized void addEntities(Collection<? extends GameEntity> entities, VisionType visionType) {
if (entities == null || entities.isEmpty()) {
return;
}
for (GameEntity entity : entities) {
this.addEntityDirectly(entity);
}
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, visionType));
}
private GameEntity removeEntityDirectly(GameEntity entity) {
return getEntities().remove(entity.getId());
}
public void removeEntity(GameEntity entity) {
this.removeEntity(entity, VisionType.VISION_TYPE_DIE);
}
public synchronized void removeEntity(GameEntity entity, VisionType visionType) {
GameEntity removed = this.removeEntityDirectly(entity);
if (removed != null) {
this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType));
}
}
public synchronized void removeEntities(List<GameEntity> entity, VisionType visionType) {
var toRemove = entity.stream()
.map(this::removeEntityDirectly)
.toList();
if (toRemove.size() > 0) {
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType));
}
}
public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) {
this.removeEntityDirectly(oldEntity);
this.addEntityDirectly(newEntity);
this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VISION_TYPE_REPLACE));
this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VISION_TYPE_REPLACE, oldEntity.getId()));
}
public void showOtherEntities(Player player) {
List<GameEntity> entities = new LinkedList<>();
GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity();
for (GameEntity entity : this.getEntities().values()) {
if (entity == currentEntity) {
continue;
}
entities.add(entity);
}
player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET));
}
public void handleAttack(AttackResult result) {
//GameEntity attacker = getEntityById(result.getAttackerId());
GameEntity target = getEntityById(result.getDefenseId());
if (target == null) {
return;
}
// Godmode check
if (target instanceof EntityAvatar) {
if (((EntityAvatar) target).getPlayer().inGodmode()) {
return;
}
}
// Sanity check
target.damage(result.getDamage(), result.getAttackerId());
}
public void killEntity(GameEntity target) {
killEntity(target, 0);
}
public void killEntity(GameEntity target, int attackerId) {
GameEntity attacker = null;
if (attackerId > 0) {
attacker = getEntityById(attackerId);
}
if (attacker != null) {
// Check codex
if (attacker instanceof EntityClientGadget gadgetAttacker) {
var clientGadgetOwner = getEntityById(gadgetAttacker.getOwnerEntityId());
if (clientGadgetOwner instanceof EntityAvatar) {
((EntityClientGadget) attacker).getOwner().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL);
}
} else if (attacker instanceof EntityAvatar avatarAttacker) {
avatarAttacker.getPlayer().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL);
}
}
// Packet
this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD));
// Reward drop
if (target instanceof EntityMonster && this.getSceneType() != SceneType.SCENE_DUNGEON) {
getWorld().getServer().getDropSystem().callDrop((EntityMonster) target);
}
// Remove entity from world
this.removeEntity(target);
// Death event
target.onDeath(attackerId);
}
public void onTick() {
// disable script for home
if (this.getSceneType() == SceneType.SCENE_HOME_WORLD || this.getSceneType() == SceneType.SCENE_HOME_ROOM) {
return;
}
if (this.getScriptManager().isInit()) {
this.checkBlocks();
} else {
// TEMPORARY
this.checkSpawns();
}
// Triggers
this.scriptManager.checkRegions();
if (challenge != null) {
challenge.onCheckTimeOut();
}
checkNpcGroup();
}
}
public int getEntityLevel(int baseLevel, int worldLevelOverride) {
int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel;
level = level >= 100 ? 100 : level;
level = level <= 0 ? 1 : level;
public int getEntityLevel(int baseLevel, int worldLevelOverride) {
int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel;
level = level >= 100 ? 100 : level;
level = level <= 0 ? 1 : level;
return level;
}
public void checkNpcGroup(){
return level;
}
public void checkNpcGroup() {
Set<SceneNpcBornEntry> npcBornEntries = ConcurrentHashMap.newKeySet();
for (Player player : this.getPlayers()) {
npcBornEntries.addAll(loadNpcForPlayer(player));
......@@ -458,7 +458,7 @@ public class Scene {
.map(SceneNpcBornEntry::getGroupId)
.toList();
if(toUnload.size() > 0){
if (toUnload.size() > 0) {
broadcastPacket(new PacketGroupUnloadNotify(toUnload));
Grasscutter.getLogger().debug("Unload NPC Group {}", toUnload);
}
......@@ -480,342 +480,342 @@ public class Scene {
Set<SpawnDataEntry> visible = new HashSet<>();
for (var block : loadedGridBlocks) {
var spawns = spawnLists.get(block);
if(spawns!=null) {
if (spawns!=null) {
visible.addAll(spawns);
}
}
// World level
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getWorld().getWorldLevel());
int worldLevelOverride = 0;
// World level
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getWorld().getWorldLevel());
int worldLevelOverride = 0;
if (worldLevelData != null) {
worldLevelOverride = worldLevelData.getMonsterLevel();
}
if (worldLevelData != null) {
worldLevelOverride = worldLevelData.getMonsterLevel();
}
// Todo
List<GameEntity> toAdd = new LinkedList<>();
List<GameEntity> toRemove = new LinkedList<>();
// Todo
List<GameEntity> toAdd = new LinkedList<>();
List<GameEntity> toRemove = new LinkedList<>();
var spawnedEntities = this.getSpawnedEntities();
for (SpawnDataEntry entry : visible) {
// If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it
if (!spawnedEntities.contains(entry) && !this.getDeadSpawnedEntities().contains(entry)) {
// Entity object holder
GameEntity entity = null;
// Check if spawn entry is monster or gadget
if (entry.getMonsterId() > 0) {
MonsterData data = GameData.getMonsterDataMap().get(entry.getMonsterId());
if (data == null) continue;
int level = this.getEntityLevel(entry.getLevel(), worldLevelOverride);
EntityMonster monster = new EntityMonster(this, data, entry.getPos(), level);
monster.getRotation().set(entry.getRot());
monster.setGroupId(entry.getGroup().getGroupId());
monster.setPoseId(entry.getPoseId());
monster.setConfigId(entry.getConfigId());
monster.setSpawnEntry(entry);
entity = monster;
} else if (entry.getGadgetId() > 0) {
EntityGadget gadget = new EntityGadget(this, entry.getGadgetId(), entry.getPos(), entry.getRot());
gadget.setGroupId(entry.getGroup().getGroupId());
gadget.setConfigId(entry.getConfigId());
gadget.setSpawnEntry(entry);
int state = entry.getGadgetState();
if(state>0) {
for (SpawnDataEntry entry : visible) {
// If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it
if (!spawnedEntities.contains(entry) && !this.getDeadSpawnedEntities().contains(entry)) {
// Entity object holder
GameEntity entity = null;
// Check if spawn entry is monster or gadget
if (entry.getMonsterId() > 0) {
MonsterData data = GameData.getMonsterDataMap().get(entry.getMonsterId());
if (data == null) continue;
int level = this.getEntityLevel(entry.getLevel(), worldLevelOverride);
EntityMonster monster = new EntityMonster(this, data, entry.getPos(), level);
monster.getRotation().set(entry.getRot());
monster.setGroupId(entry.getGroup().getGroupId());
monster.setPoseId(entry.getPoseId());
monster.setConfigId(entry.getConfigId());
monster.setSpawnEntry(entry);
entity = monster;
} else if (entry.getGadgetId() > 0) {
EntityGadget gadget = new EntityGadget(this, entry.getGadgetId(), entry.getPos(), entry.getRot());
gadget.setGroupId(entry.getGroup().getGroupId());
gadget.setConfigId(entry.getConfigId());
gadget.setSpawnEntry(entry);
int state = entry.getGadgetState();
if (state>0) {
gadget.setState(state);
}
gadget.buildContent();
gadget.buildContent();
gadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 99999);
gadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 99999);
gadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 99999);
gadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 99999);
gadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 99999);
gadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 99999);
entity = gadget;
}
entity = gadget;
}
if (entity == null) continue;
if (entity == null) continue;
// Add to scene and spawned list
toAdd.add(entity);
// Add to scene and spawned list
toAdd.add(entity);
spawnedEntities.add(entry);
}
}
}
}
for (GameEntity entity : this.getEntities().values()) {
var spawnEntry = entity.getSpawnEntry();
if (spawnEntry != null && !visible.contains(spawnEntry)) {
toRemove.add(entity);
for (GameEntity entity : this.getEntities().values()) {
var spawnEntry = entity.getSpawnEntry();
if (spawnEntry != null && !visible.contains(spawnEntry)) {
toRemove.add(entity);
spawnedEntities.remove(spawnEntry);
}
}
if (toAdd.size() > 0) {
toAdd.stream().forEach(this::addEntityDirectly);
this.broadcastPacket(new PacketSceneEntityAppearNotify(toAdd, VisionType.VISION_TYPE_BORN));
}
if (toRemove.size() > 0) {
toRemove.stream().forEach(this::removeEntityDirectly);
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
}
}
public List<SceneBlock> getPlayerActiveBlocks(Player player){
// consider the borders' entities of blocks, so we check if contains by index
return SceneIndexManager.queryNeighbors(getScriptManager().getBlocksIndex(),
player.getPosition().toXZDoubleArray(), Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
}
public void checkBlocks() {
Set<SceneBlock> visible = new HashSet<>();
for (Player player : this.getPlayers()) {
var blocks = getPlayerActiveBlocks(player);
visible.addAll(blocks);
}
Iterator<SceneBlock> it = this.getLoadedBlocks().iterator();
while (it.hasNext()) {
SceneBlock block = it.next();
if (!visible.contains(block)) {
it.remove();
onUnloadBlock(block);
}
}
for(var block : visible){
if (!this.getLoadedBlocks().contains(block)) {
this.onLoadBlock(block, this.getPlayers());
this.getLoadedBlocks().add(block);
}else{
// dynamic load the groups for players in a loaded block
var toLoad = this.getPlayers().stream()
.filter(p -> block.contains(p.getPosition()))
.map(p -> playerMeetGroups(p, block))
.flatMap(Collection::stream)
.toList();
onLoadGroup(toLoad);
}
}
}
public List<SceneGroup> playerMeetGroups(Player player, SceneBlock block){
List<SceneGroup> sceneGroups = SceneIndexManager.queryNeighbors(block.sceneGroupIndex, player.getPosition().toDoubleArray(),
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
List<SceneGroup> groups = sceneGroups.stream()
.filter(group -> !scriptManager.getLoadedGroupSetPerBlock().get(block.id).contains(group))
.peek(group -> scriptManager.getLoadedGroupSetPerBlock().get(block.id).add(group))
.toList();
if (groups.size() == 0) {
return List.of();
}
return groups;
}
public void onLoadBlock(SceneBlock block, List<Player> players) {
this.getScriptManager().loadBlockFromScript(block);
scriptManager.getLoadedGroupSetPerBlock().put(block.id , new HashSet<>());
// the groups form here is not added in current scene
var groups = players.stream()
.filter(player -> block.contains(player.getPosition()))
.map(p -> playerMeetGroups(p, block))
.flatMap(Collection::stream)
.toList();
onLoadGroup(groups);
Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id);
}
public void onLoadGroup(List<SceneGroup> groups){
if(groups == null || groups.isEmpty()){
return;
}
for (SceneGroup group : groups) {
// We load the script files for the groups here
this.getScriptManager().loadGroupFromScript(group);
}
// Spawn gadgets AFTER triggers are added
// TODO
var entities = new ArrayList<GameEntity>();
for (SceneGroup group : groups) {
if (group.init_config == null) {
continue;
}
// Load garbages
List<SceneGadget> garbageGadgets = group.getGarbageGadgets();
if (garbageGadgets != null) {
entities.addAll(garbageGadgets.stream().map(g -> scriptManager.createGadget(group.id, group.block_id, g))
.filter(Objects::nonNull)
.toList());
}
// Load suites
int suite = group.init_config.suite;
if (suite == 0 || group.suites == null || group.suites.size() == 0) {
continue;
}
// just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc.
var suiteData = group.getSuiteByIndex(suite);
suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger);
entities.addAll(scriptManager.getGadgetsInGroupSuite(group, suiteData));
entities.addAll(scriptManager.getMonstersInGroupSuite(group, suiteData));
}
}
if (toAdd.size() > 0) {
toAdd.stream().forEach(this::addEntityDirectly);
this.broadcastPacket(new PacketSceneEntityAppearNotify(toAdd, VisionType.VISION_TYPE_BORN));
}
if (toRemove.size() > 0) {
toRemove.stream().forEach(this::removeEntityDirectly);
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
}
}
public List<SceneBlock> getPlayerActiveBlocks(Player player) {
// consider the borders' entities of blocks, so we check if contains by index
return SceneIndexManager.queryNeighbors(getScriptManager().getBlocksIndex(),
player.getPosition().toXZDoubleArray(), Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
}
public void checkBlocks() {
Set<SceneBlock> visible = new HashSet<>();
for (Player player : this.getPlayers()) {
var blocks = getPlayerActiveBlocks(player);
visible.addAll(blocks);
}
Iterator<SceneBlock> it = this.getLoadedBlocks().iterator();
while (it.hasNext()) {
SceneBlock block = it.next();
if (!visible.contains(block)) {
it.remove();
onUnloadBlock(block);
}
}
for (var block : visible) {
if (!this.getLoadedBlocks().contains(block)) {
this.onLoadBlock(block, this.getPlayers());
this.getLoadedBlocks().add(block);
}else {
// dynamic load the groups for players in a loaded block
var toLoad = this.getPlayers().stream()
.filter(p -> block.contains(p.getPosition()))
.map(p -> playerMeetGroups(p, block))
.flatMap(Collection::stream)
.toList();
onLoadGroup(toLoad);
}
}
}
public List<SceneGroup> playerMeetGroups(Player player, SceneBlock block) {
List<SceneGroup> sceneGroups = SceneIndexManager.queryNeighbors(block.sceneGroupIndex, player.getPosition().toDoubleArray(),
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
List<SceneGroup> groups = sceneGroups.stream()
.filter(group -> !scriptManager.getLoadedGroupSetPerBlock().get(block.id).contains(group))
.peek(group -> scriptManager.getLoadedGroupSetPerBlock().get(block.id).add(group))
.toList();
if (groups.size() == 0) {
return List.of();
}
return groups;
}
public void onLoadBlock(SceneBlock block, List<Player> players) {
this.getScriptManager().loadBlockFromScript(block);
scriptManager.getLoadedGroupSetPerBlock().put(block.id , new HashSet<>());
// the groups form here is not added in current scene
var groups = players.stream()
.filter(player -> block.contains(player.getPosition()))
.map(p -> playerMeetGroups(p, block))
.flatMap(Collection::stream)
.toList();
onLoadGroup(groups);
Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id);
}
public void onLoadGroup(List<SceneGroup> groups) {
if (groups == null || groups.isEmpty()) {
return;
}
for (SceneGroup group : groups) {
// We load the script files for the groups here
this.getScriptManager().loadGroupFromScript(group);
}
// Spawn gadgets AFTER triggers are added
// TODO
var entities = new ArrayList<GameEntity>();
for (SceneGroup group : groups) {
if (group.init_config == null) {
continue;
}
// Load garbages
List<SceneGadget> garbageGadgets = group.getGarbageGadgets();
if (garbageGadgets != null) {
entities.addAll(garbageGadgets.stream().map(g -> scriptManager.createGadget(group.id, group.block_id, g))
.filter(Objects::nonNull)
.toList());
}
// Load suites
int suite = group.init_config.suite;
if (suite == 0 || group.suites == null || group.suites.size() == 0) {
continue;
}
// just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc.
var suiteData = group.getSuiteByIndex(suite);
suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger);
entities.addAll(scriptManager.getGadgetsInGroupSuite(group, suiteData));
entities.addAll(scriptManager.getMonstersInGroupSuite(group, suiteData));
scriptManager.registerRegionInGroupSuite(group, suiteData);
}
scriptManager.meetEntities(entities);
//scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null);
//groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null));
Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size());
}
public void onUnloadBlock(SceneBlock block) {
List<GameEntity> toRemove = this.getEntities().values().stream()
.filter(e -> e.getBlockId() == block.id).toList();
if (toRemove.size() > 0) {
toRemove.forEach(this::removeEntityDirectly);
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
}
for (SceneGroup group : block.groups.values()) {
if(group.triggers != null){
group.triggers.values().forEach(getScriptManager()::deregisterTrigger);
}
if(group.regions != null){
}
scriptManager.meetEntities(entities);
//scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null);
//groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null));
Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size());
}
public void onUnloadBlock(SceneBlock block) {
List<GameEntity> toRemove = this.getEntities().values().stream()
.filter(e -> e.getBlockId() == block.id).toList();
if (toRemove.size() > 0) {
toRemove.forEach(this::removeEntityDirectly);
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
}
for (SceneGroup group : block.groups.values()) {
if (group.triggers != null) {
group.triggers.values().forEach(getScriptManager()::deregisterTrigger);
}
if (group.regions != null) {
group.regions.values().forEach(getScriptManager()::deregisterRegion);
}
}
scriptManager.getLoadedGroupSetPerBlock().remove(block.id);
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
}
// 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) {
GameEntity 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.VISION_TYPE_DIE));
}
// Broadcasting
public void broadcastPacket(BasePacket packet) {
// Send to all players - might have to check if player has been sent data packets
for (Player player : this.getPlayers()) {
player.getSession().send(packet);
}
}
public void broadcastPacketToOthers(Player excludedPlayer, BasePacket 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 (Player player : this.getPlayers()) {
if (player == excludedPlayer) {
continue;
}
// Send
player.getSession().send(packet);
}
}
public void addItemEntity(int itemId, int amount, GameEntity bornForm){
ItemData itemData = GameData.getItemDataMap().get(itemId);
if (itemData == null) {
return;
}
if (itemData.isEquip()) {
float range = (3f + (.1f * amount));
for (int i = 0; i < amount; i++) {
Position pos = bornForm.getPosition().clone().addX((float) (Math.random() * range) - (range / 2)).addZ((float) (Math.random() * range) - (range / 2)).addZ(.9f);
EntityItem entity = new EntityItem(this, null, itemData, pos, 1);
addEntity(entity);
}
} else {
EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addZ(.9f), amount);
addEntity(entity);
}
}
public void loadNpcForPlayerEnter(Player player){
}
}
scriptManager.getLoadedGroupSetPerBlock().remove(block.id);
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
}
// 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) {
GameEntity 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.VISION_TYPE_DIE));
}
// Broadcasting
public void broadcastPacket(BasePacket packet) {
// Send to all players - might have to check if player has been sent data packets
for (Player player : this.getPlayers()) {
player.getSession().send(packet);
}
}
public void broadcastPacketToOthers(Player excludedPlayer, BasePacket 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 (Player player : this.getPlayers()) {
if (player == excludedPlayer) {
continue;
}
// Send
player.getSession().send(packet);
}
}
public void addItemEntity(int itemId, int amount, GameEntity bornForm) {
ItemData itemData = GameData.getItemDataMap().get(itemId);
if (itemData == null) {
return;
}
if (itemData.isEquip()) {
float range = (3f + (.1f * amount));
for (int i = 0; i < amount; i++) {
Position pos = bornForm.getPosition().clone().addX((float) (Math.random() * range) - (range / 2)).addZ((float) (Math.random() * range) - (range / 2)).addZ(.9f);
EntityItem entity = new EntityItem(this, null, itemData, pos, 1);
addEntity(entity);
}
} else {
EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addZ(.9f), amount);
addEntity(entity);
}
}
public void loadNpcForPlayerEnter(Player player) {
this.npcBornEntrySet.addAll(loadNpcForPlayer(player));
}
private List<SceneNpcBornEntry> loadNpcForPlayer(Player player){
var pos = player.getPosition();
var data = GameData.getSceneNpcBornData().get(getId());
if(data == null){
return List.of();
}
private List<SceneNpcBornEntry> loadNpcForPlayer(Player player) {
var pos = player.getPosition();
var data = GameData.getSceneNpcBornData().get(getId());
if (data == null) {
return List.of();
}
var npcList = SceneIndexManager.queryNeighbors(data.getIndex(), pos.toDoubleArray(),
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
var npcList = SceneIndexManager.queryNeighbors(data.getIndex(), pos.toDoubleArray(),
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
var sceneNpcBornEntries = npcList.stream()
var sceneNpcBornEntries = npcList.stream()
.filter(i -> !this.npcBornEntrySet.contains(i))
.toList();
if(sceneNpcBornEntries.size() > 0){
this.broadcastPacket(new PacketGroupSuiteNotify(sceneNpcBornEntries));
if (sceneNpcBornEntries.size() > 0) {
this.broadcastPacket(new PacketGroupSuiteNotify(sceneNpcBornEntries));
Grasscutter.getLogger().debug("Loaded Npc Group Suite {}", sceneNpcBornEntries);
}
}
return npcList;
}
}
public void loadGroupForQuest(List<QuestGroupSuite> sceneGroupSuite) {
if(!scriptManager.isInit()){
if (!scriptManager.isInit()) {
return;
}
sceneGroupSuite.forEach(i -> {
var group = scriptManager.getGroupById(i.getGroup());
if(group == null){
if (group == null) {
return;
}
var suite = group.getSuiteByIndex(i.getSuite());
if(suite == null){
if (suite == null) {
return;
}
scriptManager.addGroupSuite(group, suite);
......
......@@ -7,62 +7,62 @@ import emu.grasscutter.data.GameDepot;
import emu.grasscutter.utils.Position;
public class SpawnDataEntry {
private transient SpawnGroupEntry group;
private int monsterId;
private int gadgetId;
private int configId;
private int level;
private int poseId;
private int gatherItemId;
private transient SpawnGroupEntry group;
private int monsterId;
private int gadgetId;
private int configId;
private int level;
private int poseId;
private int gatherItemId;
private int gadgetState;
private Position pos;
private Position rot;
private Position pos;
private Position rot;
public SpawnGroupEntry getGroup() {
return group;
}
public SpawnGroupEntry getGroup() {
return group;
}
public void setGroup(SpawnGroupEntry group) {
this.group = group;
}
public void setGroup(SpawnGroupEntry group) {
this.group = group;
}
public int getMonsterId() {
return monsterId;
}
public int getMonsterId() {
return monsterId;
}
public int getGadgetId() {
return gadgetId;
}
public int getGadgetId() {
return gadgetId;
}
public int getGadgetState() {
return gadgetState;
}
public int getConfigId() {
return configId;
}
return configId;
}
public int getLevel() {
return level;
}
public int getLevel() {
return level;
}
public int getPoseId() {
return poseId;
}
public int getPoseId() {
return poseId;
}
public int getGatherItemId() {
return gatherItemId;
}
public int getGatherItemId() {
return gatherItemId;
}
public Position getPos() {
return pos;
}
public Position getPos() {
return pos;
}
public Position getRot() {
return rot;
}
public Position getRot() {
return rot;
}
public GridBlockId getBlockId(){
public GridBlockId getBlockId() {
int scale = GridBlockId.getScale(gadgetId);
return new GridBlockId(group.sceneId,scale,
(int)(pos.getX() / GameDepot.BLOCK_SIZE[scale]),
......@@ -70,32 +70,32 @@ public class SpawnDataEntry {
);
}
public static class SpawnGroupEntry {
private int sceneId;
private int groupId;
private int blockId;
private List<SpawnDataEntry> spawns;
public static class SpawnGroupEntry {
private int sceneId;
private int groupId;
private int blockId;
private List<SpawnDataEntry> spawns;
public int getSceneId() {
return sceneId;
}
public int getSceneId() {
return sceneId;
}
public int getGroupId() {
return groupId;
}
public int getGroupId() {
return groupId;
}
public int getBlockId() {
return blockId;
}
public int getBlockId() {
return blockId;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
public List<SpawnDataEntry> getSpawns() {
return spawns;
}
}
public List<SpawnDataEntry> getSpawns() {
return spawns;
}
}
public static class GridBlockId {
int sceneId;
......@@ -133,7 +133,7 @@ public class SpawnDataEntry {
return Objects.hash(sceneId, scale, x, z);
}
public static GridBlockId[] getAdjacentGridBlockIds(int sceneId, Position pos){
public static GridBlockId[] getAdjacentGridBlockIds(int sceneId, Position pos) {
GridBlockId[] results = new GridBlockId[5*5*GameDepot.BLOCK_SIZE.length];
int t=0;
for (int scale = 0; scale < GameDepot.BLOCK_SIZE.length; scale++) {
......@@ -148,7 +148,7 @@ public class SpawnDataEntry {
return results;
}
public static int getScale(int gadgetId){
public static int getScale(int gadgetId) {
return 0;//you should implement here,this is index of GameDepot.BLOCK_SIZE
}
}
......
......@@ -31,298 +31,298 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class World implements Iterable<Player> {
private final GameServer server;
private final Player owner;
private final List<Player> players;
private final Int2ObjectMap<Scene> scenes;
private int levelEntityId;
private int nextEntityId = 0;
private int nextPeerId = 0;
private int worldLevel;
private boolean isMultiplayer;
public World(Player player) {
this(player, false);
}
public World(Player player, boolean isMultiplayer) {
this.owner = player;
this.server = player.getServer();
this.players = Collections.synchronizedList(new ArrayList<>());
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
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;
}
public int getHostPeerId() {
if (this.getHost() == null) {
return 0;
}
return this.getHost().getPeerId();
}
public int getNextPeerId() {
return ++this.nextPeerId;
}
public int getWorldLevel() {
return worldLevel;
}
public void setWorldLevel(int worldLevel) {
this.worldLevel = worldLevel;
}
public List<Player> getPlayers() {
return players;
}
public Int2ObjectMap<Scene> getScenes() {
return this.scenes;
}
public Scene getSceneById(int sceneId) {
// Get scene normally
Scene scene = getScenes().get(sceneId);
if (scene != null) {
return scene;
}
// Create scene from scene data if it doesnt exist
SceneData sceneData = GameData.getSceneDataMap().get(sceneId);
if (sceneData != null) {
scene = new Scene(this, sceneData);
this.registerScene(scene);
return scene;
}
return null;
}
public int getPlayerCount() {
return getPlayers().size();
}
public boolean isMultiplayer() {
return isMultiplayer;
}
public int getNextEntityId(EntityIdType idType) {
return (idType.getId() << 24) + ++this.nextEntityId;
}
public synchronized void addPlayer(Player player) {
// Check if player already in
if (getPlayers().contains(player)) {
return;
}
// Remove player from prev world
if (player.getWorld() != null) {
player.getWorld().removePlayer(player);
}
// Register
player.setWorld(this);
getPlayers().add(player);
// Set player variables
player.setPeerId(this.getNextPeerId());
player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
// Copy main team to mp team
if (this.isMultiplayer()) {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().setCurrentCharacterIndex(0);
}
// Add to scene
Scene scene = this.getSceneById(player.getSceneId());
scene.addPlayer(player);
// Info packet for other players
if (this.getPlayers().size() > 1) {
this.updatePlayerInfos(player);
}
}
public synchronized void removePlayer(Player player) {
// Remove team entities
player.sendPacket(
new PacketDelTeamEntityNotify(
player.getSceneId(),
getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
)
);
// Deregister
getPlayers().remove(player);
player.setWorld(null);
// Remove from scene
Scene scene = this.getSceneById(player.getSceneId());
scene.removePlayer(player);
// Info packet for other players
if (this.getPlayers().size() > 0) {
this.updatePlayerInfos(player);
}
// Disband world if host leaves
if (getHost() == player) {
List<Player> kicked = new ArrayList<>(this.getPlayers());
for (Player victim : kicked) {
World world = new World(victim);
world.addPlayer(victim);
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getSceneId(), victim.getPosition()));
}
}
}
public void registerScene(Scene scene) {
this.getScenes().put(scene.getId(), scene);
}
public void deregisterScene(Scene scene) {
this.getScenes().remove(scene.getId());
}
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
return transferPlayerToScene(player, sceneId, null, pos);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
return transferPlayerToScene(player, sceneId, data, null);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
if (GameData.getSceneDataMap().get(sceneId) == null) {
return false;
}
Scene oldScene = null;
if (player.getScene() != null) {
oldScene = player.getScene();
// Dont deregister scenes if the player is going to tp back into them
if (oldScene.getId() == sceneId) {
oldScene.setDontDestroyWhenEmpty(true);
}
oldScene.removePlayer(player);
}
Scene newScene = this.getSceneById(sceneId);
newScene.setDungeonData(dungeonData);
newScene.addPlayer(player);
// Dungeon
SceneConfig config = newScene.getScriptManager().getConfig();
if (pos == null && config != null) {
if (config.born_pos != null) {
pos = newScene.getScriptManager().getConfig().born_pos;
}
if (config.born_rot != null) {
player.getRotation().set(config.born_rot);
}
}
// Set player position
if (pos == null) {
pos = player.getPosition();
}
player.getPosition().set(pos);
if (oldScene != null) {
newScene.setPrevScene(oldScene.getId());
oldScene.setDontDestroyWhenEmpty(false);
}
// Get enter types
EnterType enterType = EnterType.ENTER_TYPE_JUMP;
EnterReason enterReason = EnterReason.TransPoint;
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;
}
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
return true;
}
private void updatePlayerInfos(Player paramPlayer) {
for (Player player : getPlayers()) {
// Dont send packets if player is loading in and filter out joining player
if (!player.hasSentAvatarDataNotify() || player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() || player == paramPlayer) {
continue;
}
// Update team of all players since max players has been changed - Probably not the best way to do it
if (this.isMultiplayer()) {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().updateTeamEntities(null);
}
// World player info packets
player.getSession().send(new PacketWorldPlayerInfoNotify(this));
player.getSession().send(new PacketScenePlayerInfoNotify(this));
player.getSession().send(new PacketWorldPlayerRTTNotify(this));
// Team packets
player.getSession().send(new PacketSyncTeamEntityNotify(player));
player.getSession().send(new PacketSyncScenePlayTeamEntityNotify(player));
}
}
public void broadcastPacket(BasePacket packet) {
// Send to all players - might have to check if player has been sent data packets
for (Player player : this.getPlayers()) {
player.getSession().send(packet);
}
}
public void onTick() {
for (Scene scene : this.getScenes().values()) {
scene.onTick();
}
}
public void close() {
}
@Override
public Iterator<Player> iterator() {
return getPlayers().iterator();
}
private final GameServer server;
private final Player owner;
private final List<Player> players;
private final Int2ObjectMap<Scene> scenes;
private int levelEntityId;
private int nextEntityId = 0;
private int nextPeerId = 0;
private int worldLevel;
private boolean isMultiplayer;
public World(Player player) {
this(player, false);
}
public World(Player player, boolean isMultiplayer) {
this.owner = player;
this.server = player.getServer();
this.players = Collections.synchronizedList(new ArrayList<>());
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
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;
}
public int getHostPeerId() {
if (this.getHost() == null) {
return 0;
}
return this.getHost().getPeerId();
}
public int getNextPeerId() {
return ++this.nextPeerId;
}
public int getWorldLevel() {
return worldLevel;
}
public void setWorldLevel(int worldLevel) {
this.worldLevel = worldLevel;
}
public List<Player> getPlayers() {
return players;
}
public Int2ObjectMap<Scene> getScenes() {
return this.scenes;
}
public Scene getSceneById(int sceneId) {
// Get scene normally
Scene scene = getScenes().get(sceneId);
if (scene != null) {
return scene;
}
// Create scene from scene data if it doesnt exist
SceneData sceneData = GameData.getSceneDataMap().get(sceneId);
if (sceneData != null) {
scene = new Scene(this, sceneData);
this.registerScene(scene);
return scene;
}
return null;
}
public int getPlayerCount() {
return getPlayers().size();
}
public boolean isMultiplayer() {
return isMultiplayer;
}
public int getNextEntityId(EntityIdType idType) {
return (idType.getId() << 24) + ++this.nextEntityId;
}
public synchronized void addPlayer(Player player) {
// Check if player already in
if (getPlayers().contains(player)) {
return;
}
// Remove player from prev world
if (player.getWorld() != null) {
player.getWorld().removePlayer(player);
}
// Register
player.setWorld(this);
getPlayers().add(player);
// Set player variables
player.setPeerId(this.getNextPeerId());
player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
// Copy main team to mp team
if (this.isMultiplayer()) {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().setCurrentCharacterIndex(0);
}
// Add to scene
Scene scene = this.getSceneById(player.getSceneId());
scene.addPlayer(player);
// Info packet for other players
if (this.getPlayers().size() > 1) {
this.updatePlayerInfos(player);
}
}
public synchronized void removePlayer(Player player) {
// Remove team entities
player.sendPacket(
new PacketDelTeamEntityNotify(
player.getSceneId(),
getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
)
);
// Deregister
getPlayers().remove(player);
player.setWorld(null);
// Remove from scene
Scene scene = this.getSceneById(player.getSceneId());
scene.removePlayer(player);
// Info packet for other players
if (this.getPlayers().size() > 0) {
this.updatePlayerInfos(player);
}
// Disband world if host leaves
if (getHost() == player) {
List<Player> kicked = new ArrayList<>(this.getPlayers());
for (Player victim : kicked) {
World world = new World(victim);
world.addPlayer(victim);
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getSceneId(), victim.getPosition()));
}
}
}
public void registerScene(Scene scene) {
this.getScenes().put(scene.getId(), scene);
}
public void deregisterScene(Scene scene) {
this.getScenes().remove(scene.getId());
}
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
return transferPlayerToScene(player, sceneId, null, pos);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
return transferPlayerToScene(player, sceneId, data, null);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
if (GameData.getSceneDataMap().get(sceneId) == null) {
return false;
}
Scene oldScene = null;
if (player.getScene() != null) {
oldScene = player.getScene();
// Dont deregister scenes if the player is going to tp back into them
if (oldScene.getId() == sceneId) {
oldScene.setDontDestroyWhenEmpty(true);
}
oldScene.removePlayer(player);
}
Scene newScene = this.getSceneById(sceneId);
newScene.setDungeonData(dungeonData);
newScene.addPlayer(player);
// Dungeon
SceneConfig config = newScene.getScriptManager().getConfig();
if (pos == null && config != null) {
if (config.born_pos != null) {
pos = newScene.getScriptManager().getConfig().born_pos;
}
if (config.born_rot != null) {
player.getRotation().set(config.born_rot);
}
}
// Set player position
if (pos == null) {
pos = player.getPosition();
}
player.getPosition().set(pos);
if (oldScene != null) {
newScene.setPrevScene(oldScene.getId());
oldScene.setDontDestroyWhenEmpty(false);
}
// Get enter types
EnterType enterType = EnterType.ENTER_TYPE_JUMP;
EnterReason enterReason = EnterReason.TransPoint;
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;
}
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
return true;
}
private void updatePlayerInfos(Player paramPlayer) {
for (Player player : getPlayers()) {
// Dont send packets if player is loading in and filter out joining player
if (!player.hasSentAvatarDataNotify() || player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() || player == paramPlayer) {
continue;
}
// Update team of all players since max players has been changed - Probably not the best way to do it
if (this.isMultiplayer()) {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().updateTeamEntities(null);
}
// World player info packets
player.getSession().send(new PacketWorldPlayerInfoNotify(this));
player.getSession().send(new PacketScenePlayerInfoNotify(this));
player.getSession().send(new PacketWorldPlayerRTTNotify(this));
// Team packets
player.getSession().send(new PacketSyncTeamEntityNotify(player));
player.getSession().send(new PacketSyncScenePlayTeamEntityNotify(player));
}
}
public void broadcastPacket(BasePacket packet) {
// Send to all players - might have to check if player has been sent data packets
for (Player player : this.getPlayers()) {
player.getSession().send(packet);
}
}
public void onTick() {
for (Scene scene : this.getScenes().values()) {
scene.onTick();
}
}
public void close() {
}
@Override
public Iterator<Player> iterator() {
return getPlayers().iterator();
}
}
......@@ -30,7 +30,7 @@ public class WorldDataSystem extends BaseGameSystem {
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
private final Map<String, SceneGroup> sceneInvestigationGroupMap; // <sceneId_groupId, Group>
public WorldDataSystem(GameServer server){
public WorldDataSystem(GameServer server) {
super(server);
this.chestInteractHandlerMap = new HashMap<>();
this.sceneInvestigationGroupMap = new ConcurrentHashMap<>();
......@@ -38,15 +38,15 @@ public class WorldDataSystem extends BaseGameSystem {
loadChestConfig();
}
public synchronized void loadChestConfig(){
public synchronized void loadChestConfig() {
// set the special chest first
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
try(Reader reader = DataLoader.loadReader("ChestReward.json")) {
try (Reader reader = DataLoader.loadReader("ChestReward.json")) {
List<ChestReward> chestReward = Grasscutter.getGsonFactory().fromJson(
reader,
reader,
TypeToken.getParameterized(List.class, ChestReward.class).getType());
chestReward.forEach(reward ->
reward.getObjNames().forEach(
name -> chestInteractHandlerMap.putIfAbsent(name, new NormalChestInteractHandler(reward))));
......@@ -60,21 +60,21 @@ public class WorldDataSystem extends BaseGameSystem {
return chestInteractHandlerMap;
}
public RewardPreviewData getRewardByBossId(int monsterId){
public RewardPreviewData getRewardByBossId(int monsterId) {
var investigationMonsterData = GameData.getInvestigationMonsterDataMap().values().parallelStream()
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
.findFirst();
if(investigationMonsterData.isEmpty()){
if (investigationMonsterData.isEmpty()) {
return null;
}
return GameData.getRewardPreviewDataMap().get(investigationMonsterData.get().getRewardPreviewId());
}
private SceneGroup getInvestigationGroup(int sceneId, int groupId){
private SceneGroup getInvestigationGroup(int sceneId, int groupId) {
var key = sceneId + "_" + groupId;
if(!sceneInvestigationGroupMap.containsKey(key)){
if (!sceneInvestigationGroupMap.containsKey(key)) {
var group = SceneGroup.of(groupId).load(sceneId);
sceneInvestigationGroupMap.putIfAbsent(key, group);
return group;
......@@ -82,7 +82,7 @@ public class WorldDataSystem extends BaseGameSystem {
return sceneInvestigationGroupMap.get(key);
}
public int getMonsterLevel(SceneMonster monster, World world){
public int getMonsterLevel(SceneMonster monster, World world) {
// Calculate level
int level = monster.level;
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel());
......@@ -98,14 +98,14 @@ public class WorldDataSystem extends BaseGameSystem {
var sceneId = imd.getCityData().getSceneId();
var group = getInvestigationGroup(sceneId, groupId);
if(group == null || group.monsters == null){
if (group == null || group.monsters == null) {
return null;
}
var monster = group.monsters.values().stream()
.filter(x -> x.monster_id == monsterId)
.findFirst();
if(monster.isEmpty()){
if (monster.isEmpty()) {
return null;
}
......@@ -122,9 +122,9 @@ public class WorldDataSystem extends BaseGameSystem {
.setRefreshInterval(Integer.MAX_VALUE)
.setPos(monster.get().pos.toProto());
if("Boss".equals(imd.getMonsterCategory())){
if ("Boss".equals(imd.getMonsterCategory())) {
var bossChest = group.searchBossChestInGroup();
if(bossChest.isPresent()){
if (bossChest.isPresent()) {
builder.setResin(bossChest.get().resin);
builder.setBossChestNum(bossChest.get().take_num);
}
......@@ -132,9 +132,9 @@ public class WorldDataSystem extends BaseGameSystem {
return builder.build();
}
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(Player player, int cityId){
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(Player player, int cityId) {
var cityData = GameData.getCityDataMap().get(cityId);
if(cityData == null){
if (cityData == null) {
Grasscutter.getLogger().warn("City not exist {}", cityId);
return List.of();
}
......
......@@ -8,134 +8,134 @@ import emu.grasscutter.net.proto.PacketHeadOuterClass.PacketHead;
import emu.grasscutter.utils.Crypto;
public class BasePacket {
private static final int const1 = 17767; // 0x4567
private static final int const2 = -30293; // 0x89ab
private int opcode;
private boolean shouldBuildHeader = false;
private byte[] header;
private byte[] data;
// Encryption
private boolean useDispatchKey;
public boolean shouldEncrypt = true;
public BasePacket(int opcode) {
this.opcode = opcode;
}
public BasePacket(int opcode, int clientSequence) {
this.opcode = opcode;
this.buildHeader(clientSequence);
}
public BasePacket(int opcode, boolean buildHeader) {
this.opcode = opcode;
this.shouldBuildHeader = buildHeader;
}
public int getOpcode() {
return opcode;
}
public void setOpcode(int opcode) {
this.opcode = opcode;
}
public boolean useDispatchKey() {
return useDispatchKey;
}
public void setUseDispatchKey(boolean useDispatchKey) {
this.useDispatchKey = useDispatchKey;
}
public byte[] getHeader() {
return header;
}
public void setHeader(byte[] header) {
this.header = header;
}
public boolean shouldBuildHeader() {
return shouldBuildHeader;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public void setData(GeneratedMessageV3 proto) {
this.data = proto.toByteArray();
}
@SuppressWarnings("rawtypes")
public void setData(GeneratedMessageV3.Builder proto) {
this.data = proto.build().toByteArray();
}
public BasePacket buildHeader(int clientSequence) {
if (this.getHeader() != null && clientSequence == 0) {
return this;
}
setHeader(PacketHead.newBuilder().setClientSequenceId(clientSequence).setSentMs(System.currentTimeMillis()).build().toByteArray());
return this;
}
public byte[] build() {
if (getHeader() == null) {
this.header = new byte[0];
}
if (getData() == null) {
this.data = new byte[0];
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(2 + 2 + 2 + 4 + getHeader().length + getData().length + 2);
this.writeUint16(baos, const1);
this.writeUint16(baos, opcode);
this.writeUint16(baos, header.length);
this.writeUint32(baos, data.length);
this.writeBytes(baos, header);
this.writeBytes(baos, data);
this.writeUint16(baos, const2);
byte[] packet = baos.toByteArray();
if (this.shouldEncrypt) {
Crypto.xor(packet, this.useDispatchKey() ? Crypto.DISPATCH_KEY : Crypto.ENCRYPT_KEY);
}
return packet;
}
public void writeUint16(ByteArrayOutputStream baos, int i) {
// Unsigned short
private static final int const1 = 17767; // 0x4567
private static final int const2 = -30293; // 0x89ab
private int opcode;
private boolean shouldBuildHeader = false;
private byte[] header;
private byte[] data;
// Encryption
private boolean useDispatchKey;
public boolean shouldEncrypt = true;
public BasePacket(int opcode) {
this.opcode = opcode;
}
public BasePacket(int opcode, int clientSequence) {
this.opcode = opcode;
this.buildHeader(clientSequence);
}
public BasePacket(int opcode, boolean buildHeader) {
this.opcode = opcode;
this.shouldBuildHeader = buildHeader;
}
public int getOpcode() {
return opcode;
}
public void setOpcode(int opcode) {
this.opcode = opcode;
}
public boolean useDispatchKey() {
return useDispatchKey;
}
public void setUseDispatchKey(boolean useDispatchKey) {
this.useDispatchKey = useDispatchKey;
}
public byte[] getHeader() {
return header;
}
public void setHeader(byte[] header) {
this.header = header;
}
public boolean shouldBuildHeader() {
return shouldBuildHeader;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public void setData(GeneratedMessageV3 proto) {
this.data = proto.toByteArray();
}
@SuppressWarnings("rawtypes")
public void setData(GeneratedMessageV3.Builder proto) {
this.data = proto.build().toByteArray();
}
public BasePacket buildHeader(int clientSequence) {
if (this.getHeader() != null && clientSequence == 0) {
return this;
}
setHeader(PacketHead.newBuilder().setClientSequenceId(clientSequence).setSentMs(System.currentTimeMillis()).build().toByteArray());
return this;
}
public byte[] build() {
if (getHeader() == null) {
this.header = new byte[0];
}
if (getData() == null) {
this.data = new byte[0];
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(2 + 2 + 2 + 4 + getHeader().length + getData().length + 2);
this.writeUint16(baos, const1);
this.writeUint16(baos, opcode);
this.writeUint16(baos, header.length);
this.writeUint32(baos, data.length);
this.writeBytes(baos, header);
this.writeBytes(baos, data);
this.writeUint16(baos, const2);
byte[] packet = baos.toByteArray();
if (this.shouldEncrypt) {
Crypto.xor(packet, this.useDispatchKey() ? Crypto.DISPATCH_KEY : Crypto.ENCRYPT_KEY);
}
return packet;
}
public void writeUint16(ByteArrayOutputStream baos, int i) {
// Unsigned short
baos.write((byte) ((i >>> 8) & 0xFF));
baos.write((byte) (i & 0xFF));
}
public void writeUint32(ByteArrayOutputStream baos, int i) {
// Unsigned int (long)
public void writeUint32(ByteArrayOutputStream baos, int i) {
// Unsigned int (long)
baos.write((byte) ((i >>> 24) & 0xFF));
baos.write((byte) ((i >>> 16) & 0xFF));
baos.write((byte) ((i >>> 8) & 0xFF));
baos.write((byte) (i & 0xFF));
}
public void writeBytes(ByteArrayOutputStream baos, byte[] bytes) {
try {
baos.write(bytes);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
public void writeBytes(ByteArrayOutputStream baos, byte[] bytes) {
try {
baos.write(bytes);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
......@@ -1854,4 +1854,4 @@ public class PacketOpcodes {
public static final int WorldRoutineTypeCloseNotify = 3502;
public static final int WorldRoutineTypeRefreshNotify = 3525;
}
\ No newline at end of file
}
......@@ -22,7 +22,7 @@ public class PacketOpcodesUtils {
PacketOpcodes.WindSeedClientNotify,
PacketOpcodes.PlayerLuaShellNotify
);
public static final Set<Integer> LOOP_PACKETS = Set.of(
PacketOpcodes.PingReq,
PacketOpcodes.PingRsp,
......@@ -30,39 +30,39 @@ public class PacketOpcodesUtils {
PacketOpcodes.UnionCmdNotify,
PacketOpcodes.QueryPathReq
);
static {
opcodeMap = new Int2ObjectOpenHashMap<String>();
Field[] fields = PacketOpcodes.class.getFields();
static {
opcodeMap = new Int2ObjectOpenHashMap<String>();
Field[] fields = PacketOpcodes.class.getFields();
for (Field f : fields) {
if(f.getType().equals(int.class)) {
try {
opcodeMap.put(f.getInt(null), f.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
for (Field f : fields) {
if (f.getType().equals(int.class)) {
try {
opcodeMap.put(f.getInt(null), f.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static String getOpcodeName(int opcode) {
if (opcode <= 0) return "UNKNOWN";
return opcodeMap.getOrDefault(opcode, "UNKNOWN");
}
public static String getOpcodeName(int opcode) {
if (opcode <= 0) return "UNKNOWN";
return opcodeMap.getOrDefault(opcode, "UNKNOWN");
}
public static void dumpPacketIds() {
try (FileWriter writer = new FileWriter("./PacketIds_" + GameConstants.VERSION + ".json")) {
// Create sorted tree map
Map<Integer, String> packetIds = opcodeMap.int2ObjectEntrySet().stream()
.filter(e -> e.getIntKey() > 0)
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey, Int2ObjectMap.Entry::getValue, (k, v) -> v, TreeMap::new));
// Write to file
writer.write(Grasscutter.getGsonFactory().toJson(packetIds));
Grasscutter.getLogger().info("Dumped packet ids.");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void dumpPacketIds() {
try (FileWriter writer = new FileWriter("./PacketIds_" + GameConstants.VERSION + ".json")) {
// Create sorted tree map
Map<Integer, String> packetIds = opcodeMap.int2ObjectEntrySet().stream()
.filter(e -> e.getIntKey() > 0)
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey, Int2ObjectMap.Entry::getValue, (k, v) -> v, TreeMap::new));
// Write to file
writer.write(Grasscutter.getGsonFactory().toJson(packetIds));
Grasscutter.getLogger().info("Dumped packet ids.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
......@@ -17,7 +17,7 @@ import java.net.URLClassLoader;
*/
public abstract class Plugin {
private final ServerHook server = ServerHook.getInstance();
private PluginIdentifier identifier;
private URLClassLoader classLoader;
private File dataFolder;
......@@ -25,22 +25,22 @@ public abstract class Plugin {
/**
* This method is reflected into.
*
*
* Set plugin variables.
* @param identifier The plugin's identifier.
*/
private void initializePlugin(PluginIdentifier identifier, URLClassLoader classLoader) {
if(this.identifier != null) {
if (this.identifier != null) {
Grasscutter.getLogger().warn(this.identifier.name + " had a reinitialization attempt.");
return;
}
this.identifier = identifier;
this.classLoader = classLoader;
this.dataFolder = new File(PLUGIN(), identifier.name);
this.logger = LoggerFactory.getLogger(identifier.name);
if(!this.dataFolder.exists() && !this.dataFolder.mkdirs()) {
if (!this.dataFolder.exists() && !this.dataFolder.mkdirs()) {
Grasscutter.getLogger().warn("Failed to create plugin data folder for " + this.identifier.name);
return;
}
......@@ -50,7 +50,7 @@ public abstract class Plugin {
* The plugin's identifier instance.
* @return An instance of {@link PluginIdentifier}.
*/
public final PluginIdentifier getIdentifier(){
public final PluginIdentifier getIdentifier() {
return this.identifier;
}
......@@ -115,7 +115,7 @@ public abstract class Plugin {
public final Logger getLogger() {
return this.logger;
}
/* Called when the plugin is first loaded. */
public void onLoad() { }
/* Called after (most of) the server enables. */
......
......@@ -72,7 +72,7 @@ public final class PluginManager {
List<PluginData> dependencies = new ArrayList<>();
// Initialize all plugins.
for(var plugin : plugins) {
for (var plugin : plugins) {
try {
URL url = plugin.toURI().toURL();
try (URLClassLoader loader = new URLClassLoader(new URL[]{url})) {
......@@ -109,7 +109,7 @@ public final class PluginManager {
fileReader.close();
// Check if the plugin has alternate dependencies.
if(pluginConfig.loadAfter != null && pluginConfig.loadAfter.length > 0) {
if (pluginConfig.loadAfter != null && pluginConfig.loadAfter.length > 0) {
// Add the plugin to a "load later" list.
dependencies.add(new PluginData(
pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig),
......@@ -131,9 +131,9 @@ public final class PluginManager {
// Load plugins with dependencies.
int depth = 0; final int maxDepth = 30;
while(!dependencies.isEmpty()) {
while (!dependencies.isEmpty()) {
// Check if the depth is too high.
if(depth >= maxDepth) {
if (depth >= maxDepth) {
Grasscutter.getLogger().error("Failed to load plugins with dependencies.");
break;
}
......@@ -143,7 +143,7 @@ public final class PluginManager {
var pluginData = dependencies.get(0);
// Check if the plugin's dependencies are loaded.
if(!this.plugins.keySet().containsAll(List.of(pluginData.getDependencies()))) {
if (!this.plugins.keySet().containsAll(List.of(pluginData.getDependencies()))) {
depth++; // Increase depth counter.
continue; // Continue to next plugin.
}
......
......@@ -19,13 +19,13 @@ public final class PlayerHook {
private final Player player;
/**
* Hooks into the player.
* Hooks into the player.
* @param player The player to hook into.
*/
public PlayerHook(Player player) {
this.player = player;
}
/**
* Kicks a player from the server.
* TODO: Refactor to kick using a packet.
......@@ -82,7 +82,7 @@ public final class PlayerHook {
*/
public void teleport(Position position) {
this.player.getPosition().set(position);
this.player.sendPacket(new PacketPlayerEnterSceneNotify(this.player,
this.player.sendPacket(new PacketPlayerEnterSceneNotify(this.player,
EnterType.ENTER_TYPE_JUMP, EnterReason.TransPoint,
this.player.getSceneId(), position
));
......@@ -111,4 +111,4 @@ public final class PlayerHook {
public Avatar getCurrentAvatar() {
return this.getCurrentAvatarEntity().getAvatar();
}
}
\ No newline at end of file
}
......@@ -22,61 +22,61 @@ import java.util.stream.Collectors;
@ToString
@Setter
public class SceneBlock {
public int id;
public Position max;
public Position min;
public int sceneId;
public Map<Integer,SceneGroup> groups;
public RTree<SceneGroup, Geometry> sceneGroupIndex;
private transient boolean loaded; // Not an actual variable in the scripts either
public boolean isLoaded() {
return this.loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public boolean contains(Position pos) {
return pos.getX() <= this.max.getX() && pos.getX() >= this.min.getX() &&
pos.getZ() <= this.max.getZ() && pos.getZ() >= this.min.getZ();
}
public SceneBlock load(int sceneId, Bindings bindings){
if(this.loaded){
return this;
}
this.sceneId = sceneId;
public int id;
public Position max;
public Position min;
public int sceneId;
public Map<Integer,SceneGroup> groups;
public RTree<SceneGroup, Geometry> sceneGroupIndex;
private transient boolean loaded; // Not an actual variable in the scripts either
public boolean isLoaded() {
return this.loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public boolean contains(Position pos) {
return pos.getX() <= this.max.getX() && pos.getX() >= this.min.getX() &&
pos.getZ() <= this.max.getZ() && pos.getZ() >= this.min.getZ();
}
public SceneBlock load(int sceneId, Bindings bindings) {
if (this.loaded) {
return this;
}
this.sceneId = sceneId;
this.setLoaded(true);
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + "." + ScriptLoader.getScriptType()));
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + "." + ScriptLoader.getScriptType()));
if (cs == null) {
return null;
}
if (cs == null) {
return null;
}
// Eval script
try {
cs.eval(bindings);
// Eval script
try {
cs.eval(bindings);
// Set groups
// Set groups
this.groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")).stream()
.collect(Collectors.toMap(x -> x.id, y -> y));
.collect(Collectors.toMap(x -> x.id, y -> y));
this.groups.values().forEach(g -> g.block_id = this.id);
this.sceneGroupIndex = SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
} catch (ScriptException exception) {
this.sceneGroupIndex = SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
} catch (ScriptException exception) {
Grasscutter.getLogger().error("An error occurred while loading block " + this.id + " in scene " + sceneId, exception);
}
Grasscutter.getLogger().debug("Successfully loaded block {} in scene {}.", this.id, sceneId);
return this;
}
public Rectangle toRectangle() {
return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray());
}
}
\ No newline at end of file
}
Grasscutter.getLogger().debug("Successfully loaded block {} in scene {}.", this.id, sceneId);
return this;
}
public Rectangle toRectangle() {
return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray());
}
}
......@@ -21,93 +21,93 @@ import java.util.stream.Collectors;
@ToString
@Setter
public class SceneGroup {
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
public int id;
public int refresh_id;
public Position pos;
public int id;
public int refresh_id;
public Position pos;
public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
public Map<String, SceneTrigger> triggers;
public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
public Map<String, SceneTrigger> triggers;
public Map<Integer, SceneRegion> regions;
public List<SceneSuite> suites;
public List<SceneVar> variables;
public SceneBusiness business;
public SceneGarbage garbages;
public SceneInitConfig init_config;
private transient boolean loaded; // Not an actual variable in the scripts either
private transient CompiledScript script;
private transient Bindings bindings;
public static SceneGroup of(int groupId) {
var group = new SceneGroup();
group.id = groupId;
return group;
}
public boolean isLoaded() {
return this.loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public int getBusinessType() {
return this.business == null ? 0 : this.business.type;
}
public List<SceneGadget> getGarbageGadgets() {
return this.garbages == null ? null : this.garbages.gadgets;
}
public CompiledScript getScript() {
return this.script;
}
public SceneSuite getSuiteByIndex(int index) {
return this.suites.get(index - 1);
}
public Bindings getBindings() {
return this.bindings;
}
public synchronized SceneGroup load(int sceneId){
if(this.loaded){
return this;
}
// Set flag here so if there is no script, we don't call this function over and over again.
public List<SceneSuite> suites;
public List<SceneVar> variables;
public SceneBusiness business;
public SceneGarbage garbages;
public SceneInitConfig init_config;
private transient boolean loaded; // Not an actual variable in the scripts either
private transient CompiledScript script;
private transient Bindings bindings;
public static SceneGroup of(int groupId) {
var group = new SceneGroup();
group.id = groupId;
return group;
}
public boolean isLoaded() {
return this.loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public int getBusinessType() {
return this.business == null ? 0 : this.business.type;
}
public List<SceneGadget> getGarbageGadgets() {
return this.garbages == null ? null : this.garbages.gadgets;
}
public CompiledScript getScript() {
return this.script;
}
public SceneSuite getSuiteByIndex(int index) {
return this.suites.get(index - 1);
}
public Bindings getBindings() {
return this.bindings;
}
public synchronized SceneGroup load(int sceneId) {
if (this.loaded) {
return this;
}
// Set flag here so if there is no script, we don't call this function over and over again.
this.setLoaded(true);
this.bindings = ScriptLoader.getEngine().createBindings();
this.bindings = ScriptLoader.getEngine().createBindings();
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + "." + ScriptLoader.getScriptType()));
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + "." + ScriptLoader.getScriptType()));
if (cs == null) {
return this;
}
if (cs == null) {
return this;
}
this.script = cs;
this.script = cs;
// Eval script
try {
cs.eval(this.bindings);
// Eval script
try {
cs.eval(this.bindings);
// Set
// Set
this.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, this.bindings.get("monsters")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y));
.collect(Collectors.toMap(x -> x.config_id, y -> y));
this.monsters.values().forEach(m -> m.group = this);
this.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, this.bindings.get("gadgets")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y));
.collect(Collectors.toMap(x -> x.config_id, y -> y));
this.gadgets.values().forEach(m -> m.group = this);
this.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, this.bindings.get("triggers")).stream()
.collect(Collectors.toMap(x -> x.name, y -> y));
.collect(Collectors.toMap(x -> x.name, y -> y));
this.triggers.values().forEach(t -> t.currentGroup = this);
this.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, this.bindings.get("suites"));
......@@ -117,35 +117,35 @@ public class SceneGroup {
this.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, this.bindings.get("init_config"));
// Garbages // TODO: fix properly later
Object garbagesValue = this.bindings.get("garbages");
if (garbagesValue instanceof LuaValue garbagesTable) {
// Garbages // TODO: fix properly later
Object garbagesValue = this.bindings.get("garbages");
if (garbagesValue instanceof LuaValue garbagesTable) {
this.garbages = new SceneGarbage();
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
this.garbages.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
this.garbages.gadgets.forEach(m -> m.group = this);
}
}
}
}
// Add variables to suite
this.variables = ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
// Add variables to suite
this.variables = ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
// Add monsters and gadgets to suite
// Add monsters and gadgets to suite
this.suites.forEach(i -> i.init(this));
} catch (ScriptException e) {
Grasscutter.getLogger().error("An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
}
} catch (ScriptException e) {
Grasscutter.getLogger().error("An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
}
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId);
return this;
}
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId);
return this;
}
public Optional<SceneBossChest> searchBossChestInGroup() {
return this.gadgets.values().stream()
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
.map(g -> g.boss_chest)
.findFirst();
}
public Optional<SceneBossChest> searchBossChestInGroup() {
return this.gadgets.values().stream()
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
.map(g -> g.boss_chest)
.findFirst();
}
}
......@@ -33,7 +33,7 @@ public class SceneMeta {
return new SceneMeta().load(sceneId);
}
public SceneMeta load(int sceneId){
public SceneMeta load(int sceneId) {
// Get compiled script if cached
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "." + ScriptLoader.getScriptType()));
......
......@@ -2,11 +2,11 @@ package emu.grasscutter.server.game;
public abstract class BaseGameSystem {
protected final GameServer server;
public BaseGameSystem(GameServer server) {
this.server = server;
}
public GameServer getServer() {
return this.server;
}
......
......@@ -51,97 +51,97 @@ import static emu.grasscutter.utils.Language.translate;
public final class GameServer extends KcpServer {
// Game server base
private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler;
private final GameServerPacketHandler packetHandler;
private final Map<Integer, Player> players;
private final Set<World> worlds;
// Server systems
private final InventorySystem inventorySystem;
private final GachaSystem gachaSystem;
private final ShopSystem shopSystem;
private final MultiplayerSystem multiplayerSystem;
private final DungeonSystem dungeonSystem;
private final ExpeditionSystem expeditionSystem;
private final DropSystem dropSystem;
private final WorldDataSystem worldDataSystem;
private final BattlePassSystem battlePassSystem;
private final CombineManger combineSystem;
private final TowerSystem towerSystem;
private final AnnouncementSystem announcementSystem;
private final QuestSystem questSystem;
// Extra
private final ServerTaskScheduler scheduler;
private final InventorySystem inventorySystem;
private final GachaSystem gachaSystem;
private final ShopSystem shopSystem;
private final MultiplayerSystem multiplayerSystem;
private final DungeonSystem dungeonSystem;
private final ExpeditionSystem expeditionSystem;
private final DropSystem dropSystem;
private final WorldDataSystem worldDataSystem;
private final BattlePassSystem battlePassSystem;
private final CombineManger combineSystem;
private final TowerSystem towerSystem;
private final AnnouncementSystem announcementSystem;
private final QuestSystem questSystem;
// Extra
private final ServerTaskScheduler scheduler;
private final CommandMap commandMap;
private final TaskMap taskMap;
private ChatManagerHandler chatManager;
public GameServer() {
this(getAdapterInetSocketAddress());
}
public GameServer(InetSocketAddress address) {
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true, 40, 2, true);
channelConfig.setMtu(1400);
channelConfig.setSndwnd(256);
channelConfig.setRcvwnd(256);
channelConfig.setTimeoutMillis(30 * 1000);//30s
channelConfig.setUseConvChannel(true);
channelConfig.setAckNoDelay(false);
this.init(GameSessionManager.getListener(),channelConfig,address);
DungeonChallenge.initialize();
EnergyManager.initialize();
StaminaManager.initialize();
CookingManager.initialize();
CombineManger.initialize();
// Game Server base
this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.players = new ConcurrentHashMap<>();
this.worlds = Collections.synchronizedSet(new HashSet<>());
// Extra
this.scheduler = new ServerTaskScheduler();
this.commandMap = new CommandMap(true);
private ChatManagerHandler chatManager;
public GameServer() {
this(getAdapterInetSocketAddress());
}
public GameServer(InetSocketAddress address) {
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true, 40, 2, true);
channelConfig.setMtu(1400);
channelConfig.setSndwnd(256);
channelConfig.setRcvwnd(256);
channelConfig.setTimeoutMillis(30 * 1000);//30s
channelConfig.setUseConvChannel(true);
channelConfig.setAckNoDelay(false);
this.init(GameSessionManager.getListener(),channelConfig,address);
DungeonChallenge.initialize();
EnergyManager.initialize();
StaminaManager.initialize();
CookingManager.initialize();
CombineManger.initialize();
// Game Server base
this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.players = new ConcurrentHashMap<>();
this.worlds = Collections.synchronizedSet(new HashSet<>());
// Extra
this.scheduler = new ServerTaskScheduler();
this.commandMap = new CommandMap(true);
this.taskMap = new TaskMap(true);
// Create game systems
this.inventorySystem = new InventorySystem(this);
this.gachaSystem = new GachaSystem(this);
this.shopSystem = new ShopSystem(this);
this.multiplayerSystem = new MultiplayerSystem(this);
this.dungeonSystem = new DungeonSystem(this);
this.dropSystem = new DropSystem(this);
this.expeditionSystem = new ExpeditionSystem(this);
this.combineSystem = new CombineManger(this);
this.towerSystem = new TowerSystem(this);
this.worldDataSystem = new WorldDataSystem(this);
this.battlePassSystem = new BattlePassSystem(this);
this.announcementSystem = new AnnouncementSystem(this);
this.questSystem = new QuestSystem(this);
// Chata manager
this.chatManager = new ChatManager(this);
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
}
// Create game systems
this.inventorySystem = new InventorySystem(this);
this.gachaSystem = new GachaSystem(this);
this.shopSystem = new ShopSystem(this);
this.multiplayerSystem = new MultiplayerSystem(this);
this.dungeonSystem = new DungeonSystem(this);
this.dropSystem = new DropSystem(this);
this.expeditionSystem = new ExpeditionSystem(this);
this.combineSystem = new CombineManger(this);
this.towerSystem = new TowerSystem(this);
this.worldDataSystem = new WorldDataSystem(this);
this.battlePassSystem = new BattlePassSystem(this);
this.announcementSystem = new AnnouncementSystem(this);
this.questSystem = new QuestSystem(this);
// Chata manager
this.chatManager = new ChatManager(this);
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
}
@Deprecated
public ChatManagerHandler getChatManager() {
return chatManager;
}
@Deprecated
public void setChatManager(ChatManagerHandler chatManager) {
this.chatManager = chatManager;
}
public ChatManagerHandler getChatSystem() {
return chatManager;
}
......@@ -150,71 +150,71 @@ public final class GameServer extends KcpServer {
this.chatManager = chatManager;
}
private static InetSocketAddress getAdapterInetSocketAddress(){
InetSocketAddress inetSocketAddress;
if(GAME_INFO.bindAddress.equals("")){
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
}else{
inetSocketAddress=new InetSocketAddress(
GAME_INFO.bindAddress,
GAME_INFO.bindPort
);
}
return inetSocketAddress;
}
public void registerPlayer(Player player) {
getPlayers().put(player.getUid(), player);
}
public Player getPlayerByUid(int id) {
return this.getPlayerByUid(id, false);
}
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
// Console check
if (id == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
// Get from online players
Player player = this.getPlayers().get(id);
if (!allowOfflinePlayers) {
return player;
}
// Check database if character isnt here
if (player == null) {
player = DatabaseHelper.getPlayerByUid(id);
}
return player;
}
public Player getPlayerByAccountId(String accountId) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
return playerOpt.orElse(null);
}
public SocialDetail.Builder getSocialDetailByUid(int id) {
// Get from online players
Player player = this.getPlayerByUid(id, true);
if (player == null) {
return null;
}
return player.getSocialDetail();
}
public Account getAccountByName(String username) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
if (playerOpt.isPresent()) {
return playerOpt.get().getAccount();
}
return DatabaseHelper.getAccountByName(username);
}
private static InetSocketAddress getAdapterInetSocketAddress() {
InetSocketAddress inetSocketAddress;
if (GAME_INFO.bindAddress.equals("")) {
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
}else {
inetSocketAddress=new InetSocketAddress(
GAME_INFO.bindAddress,
GAME_INFO.bindPort
);
}
return inetSocketAddress;
}
public void registerPlayer(Player player) {
getPlayers().put(player.getUid(), player);
}
public Player getPlayerByUid(int id) {
return this.getPlayerByUid(id, false);
}
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
// Console check
if (id == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
// Get from online players
Player player = this.getPlayers().get(id);
if (!allowOfflinePlayers) {
return player;
}
// Check database if character isnt here
if (player == null) {
player = DatabaseHelper.getPlayerByUid(id);
}
return player;
}
public Player getPlayerByAccountId(String accountId) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
return playerOpt.orElse(null);
}
public SocialDetail.Builder getSocialDetailByUid(int id) {
// Get from online players
Player player = this.getPlayerByUid(id, true);
if (player == null) {
return null;
}
return player.getSocialDetail();
}
public Account getAccountByName(String username) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
if (playerOpt.isPresent()) {
return playerOpt.get().getAccount();
}
return DatabaseHelper.getAccountByName(username);
}
public synchronized void onTick() {
var tickStart = Instant.now();
......@@ -244,43 +244,43 @@ public final class GameServer extends KcpServer {
event.call();
}
public void registerWorld(World world) {
this.getWorlds().add(world);
}
public void deregisterWorld(World world) {
// TODO Auto-generated method stub
}
public void start() {
// Schedule game loop.
Timer gameLoop = new Timer();
gameLoop.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
onTick();
} catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
}
}
}, new Date(), 1000L);
Grasscutter.getLogger().info(translate("messages.status.free_software"));
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call();
}
public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
// Kick and save all players
List<Player> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values());
for (Player player : list) {
player.getSession().close();
}
}
public void registerWorld(World world) {
this.getWorlds().add(world);
}
public void deregisterWorld(World world) {
// TODO Auto-generated method stub
}
public void start() {
// Schedule game loop.
Timer gameLoop = new Timer();
gameLoop.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
onTick();
} catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
}
}
}, new Date(), 1000L);
Grasscutter.getLogger().info(translate("messages.status.free_software"));
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call();
}
public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
// Kick and save all players
List<Player> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values());
for (Player player : list) {
player.getSession().close();
}
}
}
......@@ -18,84 +18,84 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@SuppressWarnings("unchecked")
public class GameServerPacketHandler {
private final Int2ObjectMap<PacketHandler> handlers;
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
this.handlers = new Int2ObjectOpenHashMap<>();
this.registerHandlers(handlerClass);
}
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
for (Object obj : handlerClasses) {
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
}
// Debug
Grasscutter.getLogger().debug("Registered " + this.handlers.size() + " " + handlerClass.getSimpleName() + "s");
}
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
PacketHandler handler = this.handlers.get(opcode);
if (handler != null) {
try {
// Make sure session is ready for packets
SessionState state = session.getState();
if (opcode == PacketOpcodes.PingReq) {
// Always continue if packet is ping request
} else if (opcode == PacketOpcodes.GetPlayerTokenReq) {
if (state != SessionState.WAITING_FOR_TOKEN) {
return;
}
} else if (opcode == PacketOpcodes.PlayerLoginReq) {
if (state != SessionState.WAITING_FOR_LOGIN) {
return;
}
} else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
if (state != SessionState.PICKING_CHARACTER) {
return;
}
} else {
if (state != SessionState.ACTIVE) {
return;
}
}
// Invoke event.
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
if(!event.isCanceled()) // If event is not canceled, continue.
handler.handle(session, header, event.getPacketData());
} catch (Exception ex) {
// TODO Remove this when no more needed
ex.printStackTrace();
}
return; // Packet successfully handled
}
// Log unhandled packets
if (GAME_INFO.logPackets == ServerDebugMode.MISSING) {
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode));
}
}
private final Int2ObjectMap<PacketHandler> handlers;
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
this.handlers = new Int2ObjectOpenHashMap<>();
this.registerHandlers(handlerClass);
}
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
for (Object obj : handlerClasses) {
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
}
// Debug
Grasscutter.getLogger().debug("Registered " + this.handlers.size() + " " + handlerClass.getSimpleName() + "s");
}
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
PacketHandler handler = this.handlers.get(opcode);
if (handler != null) {
try {
// Make sure session is ready for packets
SessionState state = session.getState();
if (opcode == PacketOpcodes.PingReq) {
// Always continue if packet is ping request
} else if (opcode == PacketOpcodes.GetPlayerTokenReq) {
if (state != SessionState.WAITING_FOR_TOKEN) {
return;
}
} else if (opcode == PacketOpcodes.PlayerLoginReq) {
if (state != SessionState.WAITING_FOR_LOGIN) {
return;
}
} else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
if (state != SessionState.PICKING_CHARACTER) {
return;
}
} else {
if (state != SessionState.ACTIVE) {
return;
}
}
// Invoke event.
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
if (!event.isCanceled()) // If event is not canceled, continue.
handler.handle(session, header, event.getPacketData());
} catch (Exception ex) {
// TODO Remove this when no more needed
ex.printStackTrace();
}
return; // Packet successfully handled
}
// Log unhandled packets
if (GAME_INFO.logPackets == ServerDebugMode.MISSING) {
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode));
}
}
}
......@@ -21,108 +21,108 @@ import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
public class GameSession implements GameSessionManager.KcpChannel {
private final GameServer server;
private GameSessionManager.KcpTunnel tunnel;
private Account account;
private Player player;
private boolean useSecretKey;
private SessionState state;
private int clientTime;
private long lastPingTime;
private int lastClientSeq = 10;
public GameSession(GameServer server) {
this.server = server;
this.state = SessionState.WAITING_FOR_TOKEN;
this.lastPingTime = System.currentTimeMillis();
}
public GameServer getServer() {
return server;
}
public InetSocketAddress getAddress() {
try{
return tunnel.getAddress();
}catch (Throwable ignore){
return null;
}
}
public boolean useSecretKey() {
return useSecretKey;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountId() {
return this.getAccount().getId();
}
public Player getPlayer() {
return player;
}
public synchronized void setPlayer(Player player) {
this.player = player;
this.player.setSession(this);
this.player.setAccount(this.getAccount());
}
public SessionState getState() {
return state;
}
public void setState(SessionState state) {
this.state = state;
}
public boolean isLoggedIn() {
return this.getPlayer() != null;
}
public void setUseSecretKey(boolean useSecretKey) {
this.useSecretKey = useSecretKey;
}
public int getClientTime() {
return this.clientTime;
}
public long getLastPingTime() {
return lastPingTime;
}
public void updateLastPingTime(int clientTime) {
this.clientTime = clientTime;
this.lastPingTime = System.currentTimeMillis();
}
public int getNextClientSequence() {
return ++lastClientSeq;
}
private final GameServer server;
private GameSessionManager.KcpTunnel tunnel;
private Account account;
private Player player;
private boolean useSecretKey;
private SessionState state;
private int clientTime;
private long lastPingTime;
private int lastClientSeq = 10;
public GameSession(GameServer server) {
this.server = server;
this.state = SessionState.WAITING_FOR_TOKEN;
this.lastPingTime = System.currentTimeMillis();
}
public GameServer getServer() {
return server;
}
public InetSocketAddress getAddress() {
try {
return tunnel.getAddress();
}catch (Throwable ignore) {
return null;
}
}
public boolean useSecretKey() {
return useSecretKey;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountId() {
return this.getAccount().getId();
}
public Player getPlayer() {
return player;
}
public synchronized void setPlayer(Player player) {
this.player = player;
this.player.setSession(this);
this.player.setAccount(this.getAccount());
}
public SessionState getState() {
return state;
}
public void setState(SessionState state) {
this.state = state;
}
public boolean isLoggedIn() {
return this.getPlayer() != null;
}
public void setUseSecretKey(boolean useSecretKey) {
this.useSecretKey = useSecretKey;
}
public int getClientTime() {
return this.clientTime;
}
public long getLastPingTime() {
return lastPingTime;
}
public void updateLastPingTime(int clientTime) {
this.clientTime = clientTime;
this.lastPingTime = System.currentTimeMillis();
}
public int getNextClientSequence() {
return ++lastClientSeq;
}
public void replayPacket(int opcode, String name) {
String filePath = PACKET(name);
File p = new File(filePath);
String filePath = PACKET(name);
File p = new File(filePath);
if (!p.exists()) return;
if (!p.exists()) return;
byte[] packet = FileUtils.read(p);
byte[] packet = FileUtils.read(p);
BasePacket basePacket = new BasePacket(opcode);
basePacket.setData(packet);
BasePacket basePacket = new BasePacket(opcode);
basePacket.setData(packet);
send(basePacket);
send(basePacket);
}
public void logPacket( String sendOrRecv, int opcode, byte[] payload) {
......@@ -130,162 +130,162 @@ public class GameSession implements GameSessionManager.KcpChannel {
System.out.println(Utils.bytesToHex(payload));
}
public void send(BasePacket packet) {
// Test
if (packet.getOpcode() <= 0) {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
return;
}
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
// Stop WindSeedClientNotify from being sent for security purposes.
if(PacketOpcodesUtils.BANNED_PACKETS.contains(packet.getOpcode())) {
return;
}
// Header
if (packet.shouldBuildHeader()) {
packet.buildHeader(this.getNextClientSequence());
}
// Log
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode())) {
// Test
if (packet.getOpcode() <= 0) {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
return;
}
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
// Stop WindSeedClientNotify from being sent for security purposes.
if (PacketOpcodesUtils.BANNED_PACKETS.contains(packet.getOpcode())) {
return;
}
// Header
if (packet.shouldBuildHeader()) {
packet.buildHeader(this.getNextClientSequence());
}
// Log
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
case BLACKLIST-> {
}
case BLACKLIST-> {
if (!SERVER.debugBlacklist.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
default -> {}
}
// Invoke event.
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
if(!event.isCanceled()) { // If event is not cancelled, continue.
tunnel.writeData(event.getPacket().build());
}
default -> {}
}
// Invoke event.
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
if (!event.isCanceled()) { // If event is not cancelled, continue.
tunnel.writeData(event.getPacket().build());
}
}
@Override
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
this.tunnel = tunnel;
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
}
@Override
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
this.tunnel = tunnel;
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
}
@Override
public void handleReceive(byte[] bytes) {
// Decrypt and turn back into a packet
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
// Log
//logPacket(packet);
// Handle
try {
boolean allDebug = GAME_INFO.logPackets == ServerDebugMode.ALL;
while (packet.readableBytes() > 0) {
// Length
if (packet.readableBytes() < 12) {
return;
}
// Packet sanity check
int const1 = packet.readShort();
if (const1 != 17767) {
if(allDebug){
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
}
return; // Bad packet
}
// Data
int opcode = packet.readShort();
int headerLength = packet.readShort();
int payloadLength = packet.readInt();
byte[] header = new byte[headerLength];
byte[] payload = new byte[payloadLength];
packet.readBytes(header);
packet.readBytes(payload);
// Sanity check #2
int const2 = packet.readShort();
if (const2 != -30293) {
if(allDebug){
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
}
return; // Bad packet
}
// Log packet
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode)) {
@Override
public void handleReceive(byte[] bytes) {
// Decrypt and turn back into a packet
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
// Log
//logPacket(packet);
// Handle
try {
boolean allDebug = GAME_INFO.logPackets == ServerDebugMode.ALL;
while (packet.readableBytes() > 0) {
// Length
if (packet.readableBytes() < 12) {
return;
}
// Packet sanity check
int const1 = packet.readShort();
if (const1 != 17767) {
if (allDebug) {
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
}
return; // Bad packet
}
// Data
int opcode = packet.readShort();
int headerLength = packet.readShort();
int payloadLength = packet.readInt();
byte[] header = new byte[headerLength];
byte[] payload = new byte[payloadLength];
packet.readBytes(header);
packet.readBytes(payload);
// Sanity check #2
int const2 = packet.readShort();
if (const2 != -30293) {
if (allDebug) {
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
}
return; // Bad packet
}
// Log packet
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
case BLACKLIST-> {
if (!(SERVER.debugBlacklist.contains(opcode))) {
logPacket("RECV",opcode, payload);
}
}
default -> {}
}
// Handle
getServer().getPacketHandler().handle(this, opcode, header, payload);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//byteBuf.release(); //Needn't
packet.release();
}
}
@Override
public void handleClose() {
setState(SessionState.INACTIVE);
//send disconnection pack in case of reconnection
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().toString()));
// Save after disconnecting
if (this.isLoggedIn()) {
Player player = getPlayer();
// Call logout event.
player.onLogout();
}
try {
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
}catch (Throwable ignore){
Grasscutter.getLogger().warn("closing {} error",getAddress().getAddress().getHostAddress());
}
tunnel = null;
}
public void close() {
tunnel.close();
}
public boolean isActive() {
return getState() == SessionState.ACTIVE;
}
public enum SessionState {
INACTIVE,
WAITING_FOR_TOKEN,
WAITING_FOR_LOGIN,
PICKING_CHARACTER,
ACTIVE
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
case BLACKLIST-> {
if (!(SERVER.debugBlacklist.contains(opcode))) {
logPacket("RECV",opcode, payload);
}
}
default -> {}
}
// Handle
getServer().getPacketHandler().handle(this, opcode, header, payload);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//byteBuf.release(); //Needn't
packet.release();
}
}
@Override
public void handleClose() {
setState(SessionState.INACTIVE);
//send disconnection pack in case of reconnection
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().toString()));
// Save after disconnecting
if (this.isLoggedIn()) {
Player player = getPlayer();
// Call logout event.
player.onLogout();
}
try {
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
}catch (Throwable ignore) {
Grasscutter.getLogger().warn("closing {} error",getAddress().getAddress().getHostAddress());
}
tunnel = null;
}
public void close() {
tunnel.close();
}
public boolean isActive() {
return getState() == SessionState.ACTIVE;
}
public enum SessionState {
INACTIVE,
WAITING_FOR_TOKEN,
WAITING_FOR_LOGIN,
PICKING_CHARACTER,
ACTIVE
}
}
......@@ -30,22 +30,22 @@ public final class HttpServer {
this.express = new Express(config -> {
// Set the Express HTTP server.
config.server(HttpServer::createServer);
// Configure encryption/HTTPS/SSL.
config.enforceSsl = HTTP_ENCRYPTION.useEncryption;
// Configure HTTP policies.
if(HTTP_POLICIES.cors.enabled) {
if (HTTP_POLICIES.cors.enabled) {
var allowedOrigins = HTTP_POLICIES.cors.allowedOrigins;
if (allowedOrigins.length > 0)
config.enableCorsForOrigin(allowedOrigins);
else config.enableCorsForAllOrigins();
}
// Configure debug logging.
if(DISPATCH_INFO.logRequests == ServerDebugMode.ALL)
if (DISPATCH_INFO.logRequests == ServerDebugMode.ALL)
config.enableDevLogging();
// Disable compression on static files.
config.precompressStaticFiles = false;
});
......@@ -60,26 +60,26 @@ public final class HttpServer {
Server server = new Server();
ServerConnector serverConnector
= new ServerConnector(server);
if(HTTP_ENCRYPTION.useEncryption) {
if (HTTP_ENCRYPTION.useEncryption) {
var sslContextFactory = new SslContextFactory.Server();
var keystoreFile = new File(HTTP_ENCRYPTION.keystore);
if(!keystoreFile.exists()) {
if (!keystoreFile.exists()) {
HTTP_ENCRYPTION.useEncryption = false;
HTTP_ENCRYPTION.useInRouting = false;
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.no_keystore_error"));
} else try {
sslContextFactory.setKeyStorePath(keystoreFile.getPath());
sslContextFactory.setKeyStorePassword(HTTP_ENCRYPTION.keystorePassword);
} catch (Exception ignored) {
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.password_error"));
try {
sslContextFactory.setKeyStorePath(keystoreFile.getPath());
sslContextFactory.setKeyStorePassword("123456");
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.default_password"));
} catch (Exception exception) {
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.general_error"), exception);
......@@ -88,10 +88,10 @@ public final class HttpServer {
serverConnector = new ServerConnector(server, sslContextFactory);
}
}
serverConnector.setPort(HTTP_INFO.bindPort);
server.setConnectors(new ServerConnector[]{serverConnector});
return server;
}
......@@ -112,9 +112,9 @@ public final class HttpServer {
public HttpServer addRouter(Class<? extends Router> router, Object... args) {
// Get all constructor parameters.
Class<?>[] types = new Class<?>[args.length];
for(var argument : args)
for (var argument : args)
types[args.length - 1] = argument.getClass();
try { // Create a router instance & apply routes.
var constructor = router.getDeclaredConstructor(types); // Get the constructor.
var routerInstance = constructor.newInstance(args); // Create instance.
......@@ -130,9 +130,9 @@ public final class HttpServer {
*/
public void start() throws UnsupportedEncodingException {
// Attempt to start the HTTP server.
if(HTTP_INFO.bindAddress.equals("")){
if (HTTP_INFO.bindAddress.equals("")) {
this.express.listen(HTTP_INFO.bindPort);
}else{
}else {
this.express.listen(HTTP_INFO.bindAddress, HTTP_INFO.bindPort);
}
......@@ -147,7 +147,7 @@ public final class HttpServer {
@Override public void applyRoutes(Express express, Javalin handle) {
express.get("/", (request, response) -> {
File file = new File(HTTP_STATIC_FILES.indexFile);
if(!file.exists())
if (!file.exists())
response.send("""
<!DOCTYPE html>
<html>
......@@ -173,19 +173,19 @@ public final class HttpServer {
public static class UnhandledRequestRouter implements Router {
@Override public void applyRoutes(Express express, Javalin handle) {
handle.error(404, context -> {
if(DISPATCH_INFO.logRequests == ServerDebugMode.MISSING)
if (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING)
Grasscutter.getLogger().info(translate("messages.dispatch.unhandled_request_error", context.method(), context.url()));
context.contentType("text/html");
File file = new File(HTTP_STATIC_FILES.errorFile);
if(!file.exists())
if (!file.exists())
context.result("""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
</head>
<body>
<img src="https://http.cat/404" />
</body>
......
......@@ -60,7 +60,7 @@ public final class RegionHandler implements Router {
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts.
var configuredRegions = new ArrayList<>(List.of(DISPATCH_INFO.regions));
if(SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
if (SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
System.exit(1);
} else if (configuredRegions.size() == 0)
......@@ -136,11 +136,11 @@ public final class RegionHandler implements Router {
// Get region data.
String regionData = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw==";
if (request.query().values().size() > 0) {
if(region != null)
if (region != null)
regionData = region.getBase64();
}
if( versionName.contains("2.7.5") || versionName.contains("2.8.")) {
if ( versionName.contains("2.7.5") || versionName.contains("2.8.")) {
try {
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call();
......@@ -227,4 +227,4 @@ public final class RegionHandler implements Router {
public static QueryCurrRegionHttpRsp getCurrentRegion() {
return SERVER.runMode == ServerRunMode.HYBRID ? regions.get("os_usa").getRegionQuery() : null;
}
}
\ No newline at end of file
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment