mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-25 22:06:29 +00:00
Enable script in big world (#884)
* add docs for tower * fix: LEAK: ByteBuf.release() was not called * enableScriptInBigWorld * not print log when loaded scripts from cache * revert the change of server tick * revert the change of server tick * fix * optimize the performance: lazy load & cache * fix the refresh group * fix NPE Co-authored-by: Melledy <52122272+Melledy@users.noreply.github.com>
This commit is contained in:
parent
eb64b25f12
commit
6dc30e4def
@ -86,6 +86,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1'
|
implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1'
|
||||||
|
|
||||||
|
implementation group: 'ch.ethz.globis.phtree', name : 'phtree', version: '2.5.0'
|
||||||
|
|
||||||
protobuf files('proto/')
|
protobuf files('proto/')
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.24'
|
compileOnly 'org.projectlombok:lombok:1.18.24'
|
||||||
|
@ -2,7 +2,6 @@ package emu.grasscutter.game.player;
|
|||||||
|
|
||||||
import dev.morphia.annotations.*;
|
import dev.morphia.annotations.*;
|
||||||
import emu.grasscutter.GameConstants;
|
import emu.grasscutter.GameConstants;
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.def.PlayerLevelData;
|
import emu.grasscutter.data.def.PlayerLevelData;
|
||||||
import emu.grasscutter.database.DatabaseHelper;
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package emu.grasscutter.game.world;
|
package emu.grasscutter.game.world;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.GameDepot;
|
import emu.grasscutter.data.GameDepot;
|
||||||
import emu.grasscutter.data.def.DungeonData;
|
import emu.grasscutter.data.def.DungeonData;
|
||||||
@ -19,12 +20,12 @@ import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
|||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||||
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
|
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
|
||||||
|
import emu.grasscutter.scripts.SceneIndexManager;
|
||||||
import emu.grasscutter.scripts.SceneScriptManager;
|
import emu.grasscutter.scripts.SceneScriptManager;
|
||||||
import emu.grasscutter.scripts.data.SceneBlock;
|
import emu.grasscutter.scripts.data.SceneBlock;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify;
|
import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
|
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
|
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
|
||||||
@ -34,6 +35,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|||||||
import org.danilopianini.util.SpatialIndex;
|
import org.danilopianini.util.SpatialIndex;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Scene {
|
public class Scene {
|
||||||
private final World world;
|
private final World world;
|
||||||
@ -57,7 +59,6 @@ public class Scene {
|
|||||||
private DungeonData dungeonData;
|
private DungeonData dungeonData;
|
||||||
private int prevScene; // Id of the previous scene
|
private int prevScene; // Id of the previous scene
|
||||||
private int prevScenePoint;
|
private int prevScenePoint;
|
||||||
|
|
||||||
public Scene(World world, SceneData sceneData) {
|
public Scene(World world, SceneData sceneData) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.sceneData = sceneData;
|
this.sceneData = sceneData;
|
||||||
@ -323,14 +324,21 @@ public class Scene {
|
|||||||
public synchronized void addEntityToSingleClient(Player player, GameEntity entity) {
|
public synchronized void addEntityToSingleClient(Player player, GameEntity entity) {
|
||||||
this.addEntityDirectly(entity);
|
this.addEntityDirectly(entity);
|
||||||
player.sendPacket(new PacketSceneEntityAppearNotify(entity));
|
player.sendPacket(new PacketSceneEntityAppearNotify(entity));
|
||||||
|
|
||||||
|
}
|
||||||
|
public void addEntities(Collection<? extends GameEntity> entities){
|
||||||
|
addEntities(entities, VisionType.VISION_BORN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void addEntities(Collection<GameEntity> entities) {
|
public synchronized void addEntities(Collection<? extends GameEntity> entities, VisionType visionType) {
|
||||||
|
if(entities == null || entities.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (GameEntity entity : entities) {
|
for (GameEntity entity : entities) {
|
||||||
this.addEntityDirectly(entity);
|
this.addEntityDirectly(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_BORN));
|
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, visionType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private GameEntity removeEntityDirectly(GameEntity entity) {
|
private GameEntity removeEntityDirectly(GameEntity entity) {
|
||||||
@ -410,9 +418,8 @@ public class Scene {
|
|||||||
// TEMPORARY
|
// TEMPORARY
|
||||||
this.checkSpawns();
|
this.checkSpawns();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triggers
|
// Triggers
|
||||||
this.getScriptManager().onTick();
|
this.scriptManager.checkRegions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - Test
|
// TODO - Test
|
||||||
@ -484,18 +491,17 @@ public class Scene {
|
|||||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE));
|
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public Set<SceneBlock> getPlayerActiveBlocks(Player player){
|
||||||
|
// TODO consider the borders of blocks
|
||||||
|
return getScriptManager().getBlocks().values().stream()
|
||||||
|
.filter(block -> block.contains(player.getPos()))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
public void checkBlocks() {
|
public void checkBlocks() {
|
||||||
Set<SceneBlock> visible = new HashSet<>();
|
Set<SceneBlock> visible = new HashSet<>();
|
||||||
|
|
||||||
for (Player player : this.getPlayers()) {
|
for (Player player : this.getPlayers()) {
|
||||||
for (SceneBlock block : getScriptManager().getBlocks()) {
|
var blocks = getPlayerActiveBlocks(player);
|
||||||
if (!block.contains(player.getPos())) {
|
visible.addAll(blocks);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
visible.add(block);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<SceneBlock> it = this.getLoadedBlocks().iterator();
|
Iterator<SceneBlock> it = this.getLoadedBlocks().iterator();
|
||||||
@ -509,29 +515,51 @@ public class Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SceneBlock block : visible) {
|
for(var block : visible){
|
||||||
if (!this.getLoadedBlocks().contains(block)) {
|
if (!this.getLoadedBlocks().contains(block)) {
|
||||||
this.onLoadBlock(block);
|
this.onLoadBlock(block, this.getPlayers());
|
||||||
this.getLoadedBlocks().add(block);
|
this.getLoadedBlocks().add(block);
|
||||||
}
|
}
|
||||||
|
this.getPlayers().stream()
|
||||||
|
.filter(p -> block.contains(p.getPos()))
|
||||||
|
.forEach(p -> playerMeetGroups(p, block));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
public List<SceneGroup> playerMeetGroups(Player player, SceneBlock block){
|
||||||
|
int RANGE = 100;
|
||||||
|
|
||||||
// TODO optimize
|
var sceneGroups = SceneIndexManager.queryNeighbors(block.sceneGroupIndex, player.getPos(), RANGE);
|
||||||
public void onLoadBlock(SceneBlock block) {
|
|
||||||
for (SceneGroup group : block.groups) {
|
var groups = new ArrayList<>(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info("Scene {} Block {} loaded {} group(s)", this.getId(), block.id, groups.size());
|
||||||
|
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()
|
||||||
|
.map(p -> playerMeetGroups(p, block))
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
for (SceneGroup group : groups) {
|
||||||
// We load the script files for the groups here
|
// We load the script files for the groups here
|
||||||
if (!group.isLoaded()) {
|
this.getScriptManager().loadGroupFromScript(group);
|
||||||
this.getScriptManager().loadGroupFromScript(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
group.triggers.forEach(getScriptManager()::registerTrigger);
|
|
||||||
group.regions.forEach(getScriptManager()::registerRegion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn gadgets AFTER triggers are added
|
// Spawn gadgets AFTER triggers are added
|
||||||
// TODO
|
// TODO
|
||||||
for (SceneGroup group : block.groups) {
|
for (SceneGroup group : groups) {
|
||||||
if (group.init_config == null) {
|
if (group.init_config == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -543,24 +571,34 @@ public class Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
this.getScriptManager().spawnGadgetsInGroup(group, suite);
|
var suiteData = group.getSuiteByIndex(suite);
|
||||||
|
getScriptManager().spawnGadgetsInGroup(group,suiteData);
|
||||||
|
getScriptManager().spawnMonstersInGroup(group, suiteData);
|
||||||
suite++;
|
suite++;
|
||||||
} while (suite < group.init_config.end_suite);
|
} while (suite < group.init_config.end_suite);
|
||||||
}
|
}
|
||||||
|
Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUnloadBlock(SceneBlock block) {
|
public void onUnloadBlock(SceneBlock block) {
|
||||||
List<GameEntity> toRemove = this.getEntities().values().stream().filter(e -> e.getBlockId() == block.id).toList();
|
List<GameEntity> toRemove = this.getEntities().values().stream()
|
||||||
|
.filter(e -> e.getBlockId() == block.id).toList();
|
||||||
|
|
||||||
if (toRemove.size() > 0) {
|
if (toRemove.size() > 0) {
|
||||||
toRemove.stream().forEach(this::removeEntityDirectly);
|
toRemove.forEach(this::removeEntityDirectly);
|
||||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE));
|
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SceneGroup group : block.groups) {
|
for (SceneGroup group : block.groups) {
|
||||||
group.triggers.forEach(getScriptManager()::deregisterTrigger);
|
if(group.triggers != null){
|
||||||
group.regions.forEach(getScriptManager()::deregisterRegion);
|
group.triggers.forEach(getScriptManager()::deregisterTrigger);
|
||||||
|
}
|
||||||
|
if(group.regions != null){
|
||||||
|
group.regions.forEach(getScriptManager()::deregisterRegion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
scriptManager.getLoadedGroupSetPerBlock().remove(block.id);
|
||||||
|
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gadgets
|
// Gadgets
|
||||||
|
41
src/main/java/emu/grasscutter/scripts/SceneIndexManager.java
Normal file
41
src/main/java/emu/grasscutter/scripts/SceneIndexManager.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
|
import ch.ethz.globis.phtree.PhTree;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class SceneIndexManager {
|
||||||
|
|
||||||
|
public static <T> void buildIndex(PhTree<T> tree, List<T> elements, Function<T, long[]> extractor){
|
||||||
|
elements.forEach(e -> tree.put(extractor.apply(e), e));
|
||||||
|
}
|
||||||
|
public static <T> List<T> queryNeighbors(PhTree<T> tree, Position position, int range){
|
||||||
|
var result = new ArrayList<T>();
|
||||||
|
var arrPos = position.toLongArray();
|
||||||
|
var query = tree.query(calRange(arrPos, -range), calRange(arrPos, range));
|
||||||
|
while(query.hasNext()){
|
||||||
|
var element = query.next();
|
||||||
|
result.add(element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public static <T> List<T> queryNeighbors(PhTree<T> tree, long[] position, int range){
|
||||||
|
var result = new ArrayList<T>();
|
||||||
|
var query = tree.query(calRange(position, -range), calRange(position, range));
|
||||||
|
while(query.hasNext()){
|
||||||
|
var element = query.next();
|
||||||
|
result.add(element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private static long[] calRange(long[] position, int range){
|
||||||
|
var newPos = position.clone();
|
||||||
|
for(int i=0;i<position.length;i++){
|
||||||
|
newPos[i] += range;
|
||||||
|
}
|
||||||
|
return newPos;
|
||||||
|
}
|
||||||
|
}
|
@ -1,37 +1,28 @@
|
|||||||
package emu.grasscutter.scripts;
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
import java.util.*;
|
import ch.ethz.globis.phtree.PhTree;
|
||||||
import java.util.stream.Collectors;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
import javax.script.Bindings;
|
import emu.grasscutter.data.def.MonsterData;
|
||||||
import javax.script.CompiledScript;
|
import emu.grasscutter.data.def.WorldLevelData;
|
||||||
import javax.script.ScriptException;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.net.proto.VisionTypeOuterClass;
|
||||||
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
|
import emu.grasscutter.scripts.data.*;
|
||||||
import emu.grasscutter.scripts.service.ScriptMonsterSpawnService;
|
import emu.grasscutter.scripts.service.ScriptMonsterSpawnService;
|
||||||
import emu.grasscutter.scripts.service.ScriptMonsterTideService;
|
import emu.grasscutter.scripts.service.ScriptMonsterTideService;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import org.luaj.vm2.LuaError;
|
import org.luaj.vm2.LuaError;
|
||||||
import org.luaj.vm2.LuaValue;
|
import org.luaj.vm2.LuaValue;
|
||||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import javax.script.Bindings;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import javax.script.ScriptException;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import java.util.*;
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
|
||||||
import emu.grasscutter.scripts.data.SceneBlock;
|
|
||||||
import emu.grasscutter.scripts.data.SceneConfig;
|
|
||||||
import emu.grasscutter.scripts.data.SceneGadget;
|
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
|
||||||
import emu.grasscutter.scripts.data.SceneInitConfig;
|
|
||||||
import emu.grasscutter.scripts.data.SceneMonster;
|
|
||||||
import emu.grasscutter.scripts.data.SceneRegion;
|
|
||||||
import emu.grasscutter.scripts.data.SceneSuite;
|
|
||||||
import emu.grasscutter.scripts.data.SceneTrigger;
|
|
||||||
import emu.grasscutter.scripts.data.SceneVar;
|
|
||||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
|
|
||||||
import static emu.grasscutter.Configuration.*;
|
|
||||||
|
|
||||||
public class SceneScriptManager {
|
public class SceneScriptManager {
|
||||||
private final Scene scene;
|
private final Scene scene;
|
||||||
@ -39,8 +30,7 @@ public class SceneScriptManager {
|
|||||||
private final LuaValue scriptLibLua;
|
private final LuaValue scriptLibLua;
|
||||||
private final Map<String, Integer> variables;
|
private final Map<String, Integer> variables;
|
||||||
private Bindings bindings;
|
private Bindings bindings;
|
||||||
private SceneConfig config;
|
private SceneMeta meta;
|
||||||
private List<SceneBlock> blocks;
|
|
||||||
private boolean isInit;
|
private boolean isInit;
|
||||||
/**
|
/**
|
||||||
* SceneTrigger Set
|
* SceneTrigger Set
|
||||||
@ -55,7 +45,10 @@ public class SceneScriptManager {
|
|||||||
private SceneGroup currentGroup;
|
private SceneGroup currentGroup;
|
||||||
private ScriptMonsterTideService scriptMonsterTideService;
|
private ScriptMonsterTideService scriptMonsterTideService;
|
||||||
private ScriptMonsterSpawnService scriptMonsterSpawnService;
|
private ScriptMonsterSpawnService scriptMonsterSpawnService;
|
||||||
|
/**
|
||||||
|
* blockid - loaded groupSet
|
||||||
|
*/
|
||||||
|
private Int2ObjectMap<Set<SceneGroup>> loadedGroupSetPerBlock;
|
||||||
public SceneScriptManager(Scene scene) {
|
public SceneScriptManager(Scene scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.scriptLib = new ScriptLib(this);
|
this.scriptLib = new ScriptLib(this);
|
||||||
@ -67,9 +60,10 @@ public class SceneScriptManager {
|
|||||||
this.variables = new HashMap<>();
|
this.variables = new HashMap<>();
|
||||||
this.sceneGroups = new HashMap<>();
|
this.sceneGroups = new HashMap<>();
|
||||||
this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this);
|
this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this);
|
||||||
|
this.loadedGroupSetPerBlock = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
// TEMPORARY
|
// TEMPORARY
|
||||||
if (this.getScene().getId() < 10) {
|
if (this.getScene().getId() < 10 && !Grasscutter.getConfig().server.game.enableScriptInBigWorld) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,15 +88,18 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SceneConfig getConfig() {
|
public SceneConfig getConfig() {
|
||||||
return config;
|
if(!isInit){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return meta.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SceneGroup getCurrentGroup() {
|
public SceneGroup getCurrentGroup() {
|
||||||
return currentGroup;
|
return currentGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<SceneBlock> getBlocks() {
|
public Map<Integer, SceneBlock> getBlocks() {
|
||||||
return blocks;
|
return meta.blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Integer> getVariables() {
|
public Map<String, Integer> getVariables() {
|
||||||
@ -151,11 +148,18 @@ public class SceneScriptManager {
|
|||||||
regions.remove(region.config_id);
|
regions.remove(region.config_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Int2ObjectMap<Set<SceneGroup>> getLoadedGroupSetPerBlock() {
|
||||||
|
return loadedGroupSetPerBlock;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO optimize
|
// TODO optimize
|
||||||
public SceneGroup getGroupById(int groupId) {
|
public SceneGroup getGroupById(int groupId) {
|
||||||
for (SceneBlock block : this.getScene().getLoadedBlocks()) {
|
for (SceneBlock block : this.getScene().getLoadedBlocks()) {
|
||||||
for (SceneGroup group : block.groups) {
|
for (SceneGroup group : block.groups) {
|
||||||
if (group.id == groupId) {
|
if (group.id == groupId) {
|
||||||
|
if(!group.isLoaded()){
|
||||||
|
loadGroupFromScript(group);
|
||||||
|
}
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,44 +168,16 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
// Get compiled script if cached
|
|
||||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
|
||||||
SCRIPT("Scene/" + getScene().getId() + "/scene" + getScene().getId() + "." + ScriptLoader.getScriptType()));
|
|
||||||
|
|
||||||
if (cs == null) {
|
|
||||||
Grasscutter.getLogger().warn("No script found for scene " + getScene().getId());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create bindings
|
// Create bindings
|
||||||
bindings = ScriptLoader.getEngine().createBindings();
|
bindings = ScriptLoader.getEngine().createBindings();
|
||||||
|
|
||||||
// Set variables
|
// Set variables
|
||||||
bindings.put("ScriptLib", getScriptLib());
|
bindings.put("ScriptLib", getScriptLib());
|
||||||
|
|
||||||
// Eval script
|
var meta = ScriptLoader.getSceneMeta(getScene().getId());
|
||||||
try {
|
if (meta == null){
|
||||||
cs.eval(getBindings());
|
|
||||||
|
|
||||||
this.config = ScriptLoader.getSerializer().toObject(SceneConfig.class, bindings.get("scene_config"));
|
|
||||||
|
|
||||||
// TODO optimize later
|
|
||||||
// Create blocks
|
|
||||||
List<Integer> blockIds = ScriptLoader.getSerializer().toList(Integer.class, bindings.get("blocks"));
|
|
||||||
List<SceneBlock> blocks = ScriptLoader.getSerializer().toList(SceneBlock.class, bindings.get("block_rects"));
|
|
||||||
|
|
||||||
for (int i = 0; i < blocks.size(); i++) {
|
|
||||||
SceneBlock block = blocks.get(i);
|
|
||||||
block.id = blockIds.get(i);
|
|
||||||
|
|
||||||
loadBlockFromScript(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blocks = blocks;
|
|
||||||
} catch (ScriptException e) {
|
|
||||||
Grasscutter.getLogger().error("Error running script", e);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.meta = meta;
|
||||||
|
|
||||||
// TEMP
|
// TEMP
|
||||||
this.isInit = true;
|
this.isInit = true;
|
||||||
@ -211,89 +187,29 @@ public class SceneScriptManager {
|
|||||||
return isInit;
|
return isInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadBlockFromScript(SceneBlock block) {
|
public void loadBlockFromScript(SceneBlock block) {
|
||||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
block.load(scene.getId(), meta.context);
|
||||||
SCRIPT("Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_block" + block.id + "." + ScriptLoader.getScriptType()));
|
|
||||||
|
|
||||||
if (cs == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eval script
|
|
||||||
try {
|
|
||||||
cs.eval(getBindings());
|
|
||||||
|
|
||||||
// Set groups
|
|
||||||
block.groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups"));
|
|
||||||
block.groups.forEach(g -> g.block_id = block.id);
|
|
||||||
} catch (ScriptException e) {
|
|
||||||
Grasscutter.getLogger().error("Error loading block " + block.id + " in scene " + getScene().getId(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadGroupFromScript(SceneGroup group) {
|
public void loadGroupFromScript(SceneGroup group) {
|
||||||
// Set flag here so if there is no script, we dont call this function over and over again.
|
group.load(getScene().getId(), meta.context);
|
||||||
group.setLoaded(true);
|
|
||||||
|
|
||||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
|
||||||
SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_group" + group.id + "." + ScriptLoader.getScriptType());
|
|
||||||
|
|
||||||
if (cs == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eval script
|
|
||||||
try {
|
try {
|
||||||
cs.eval(getBindings());
|
// build the trigger for this scene
|
||||||
|
group.getScript().eval(getBindings());
|
||||||
// Set
|
|
||||||
group.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")).stream()
|
|
||||||
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
|
||||||
group.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets"));
|
|
||||||
group.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers"));
|
|
||||||
group.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites"));
|
|
||||||
group.regions = ScriptLoader.getSerializer().toList(SceneRegion.class, bindings.get("regions"));
|
|
||||||
group.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config"));
|
|
||||||
|
|
||||||
// Add variables to suite
|
|
||||||
List<SceneVar> variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables"));
|
|
||||||
variables.forEach(var -> this.getVariables().put(var.name, var.value));
|
|
||||||
|
|
||||||
// Add monsters to suite TODO optimize
|
|
||||||
Int2ObjectMap<Object> map = new Int2ObjectOpenHashMap<>();
|
|
||||||
group.monsters.entrySet().forEach(m -> map.put(m.getValue().config_id, m));
|
|
||||||
group.gadgets.forEach(m -> map.put(m.config_id, m));
|
|
||||||
|
|
||||||
for (SceneSuite suite : group.suites) {
|
|
||||||
suite.sceneMonsters = new ArrayList<>(suite.monsters.size());
|
|
||||||
suite.monsters.forEach(id -> {
|
|
||||||
Object objEntry = map.get(id.intValue());
|
|
||||||
if (objEntry instanceof Map.Entry<?,?> monsterEntry) {
|
|
||||||
Object monster = monsterEntry.getValue();
|
|
||||||
if(monster instanceof SceneMonster sceneMonster){
|
|
||||||
suite.sceneMonsters.add(sceneMonster);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
suite.sceneGadgets = new ArrayList<>(suite.gadgets.size());
|
|
||||||
for (int id : suite.gadgets) {
|
|
||||||
try {
|
|
||||||
SceneGadget gadget = (SceneGadget) map.get(id);
|
|
||||||
if (gadget != null) {
|
|
||||||
suite.sceneGadgets.add(gadget);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.sceneGroups.put(group.id, group);
|
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e);
|
Grasscutter.getLogger().error("Could not build the trigger for this scene", e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void onTick() {
|
group.variables.forEach(var -> this.getVariables().put(var.name, var.value));
|
||||||
checkRegions();
|
this.sceneGroups.put(group.id, group);
|
||||||
|
|
||||||
|
if(group.triggers != null){
|
||||||
|
group.triggers.forEach(this::registerTrigger);
|
||||||
|
}
|
||||||
|
if(group.regions != null){
|
||||||
|
group.regions.forEach(this::registerRegion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkRegions() {
|
public void checkRegions() {
|
||||||
@ -331,20 +247,11 @@ public class SceneScriptManager {
|
|||||||
gadgets = suite.sceneGadgets;
|
gadgets = suite.sceneGadgets;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SceneGadget g : gadgets) {
|
var toCreate = gadgets.stream()
|
||||||
EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos);
|
.map(g -> createGadgets(g.groupId, group.block_id, g))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
if (entity.getGadgetData() == null) continue;
|
.toList();
|
||||||
|
this.addEntities(toCreate);
|
||||||
entity.setBlockId(group.block_id);
|
|
||||||
entity.setConfigId(g.config_id);
|
|
||||||
entity.setGroupId(group.id);
|
|
||||||
entity.getRotation().set(g.rot);
|
|
||||||
entity.setState(g.state);
|
|
||||||
|
|
||||||
getScene().addEntity(entity);
|
|
||||||
this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) {
|
public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) {
|
||||||
@ -359,12 +266,15 @@ public class SceneScriptManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.currentGroup = group;
|
this.currentGroup = group;
|
||||||
suite.sceneMonsters.forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob));
|
this.addEntities(suite.sceneMonsters.stream()
|
||||||
|
.map(mob -> createMonster(group.id, group.block_id, mob)).toList());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spawnMonstersInGroup(SceneGroup group) {
|
public void spawnMonstersInGroup(SceneGroup group) {
|
||||||
this.currentGroup = group;
|
this.currentGroup = group;
|
||||||
group.monsters.values().forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob));
|
this.addEntities(group.monsters.values().stream()
|
||||||
|
.map(mob -> createMonster(group.id, group.block_id, mob)).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startMonsterTideInGroup(SceneGroup group, Integer[] ordersConfigId, int tideCount, int sceneLimit) {
|
public void startMonsterTideInGroup(SceneGroup group, Integer[] ordersConfigId, int tideCount, int sceneLimit) {
|
||||||
@ -381,7 +291,8 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
public void spawnMonstersByConfigId(int configId, int delayTime) {
|
public void spawnMonstersByConfigId(int configId, int delayTime) {
|
||||||
// TODO delay
|
// TODO delay
|
||||||
this.scriptMonsterSpawnService.spawnMonster(this.currentGroup.id, this.currentGroup.monsters.get(configId));
|
getScene().addEntity(
|
||||||
|
createMonster(this.currentGroup.id, this.currentGroup.block_id, this.currentGroup.monsters.get(configId)));
|
||||||
}
|
}
|
||||||
// Events
|
// Events
|
||||||
|
|
||||||
@ -407,6 +318,9 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ret.isboolean() && ret.checkboolean()) {
|
if (ret.isboolean() && ret.checkboolean()) {
|
||||||
|
if(trigger.action == null || trigger.action.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
ScriptLib.logger.trace("Call Action Trigger {}", trigger);
|
ScriptLib.logger.trace("Call Action Trigger {}", trigger);
|
||||||
LuaValue action = (LuaValue) this.getBindings().get(trigger.action);
|
LuaValue action = (LuaValue) this.getBindings().get(trigger.action);
|
||||||
// TODO impl the param of SetGroupVariableValueByGroup
|
// TODO impl the param of SetGroupVariableValueByGroup
|
||||||
@ -436,4 +350,84 @@ public class SceneScriptManager {
|
|||||||
return scriptMonsterSpawnService;
|
return scriptMonsterSpawnService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EntityGadget createGadgets(int groupId, int blockId, SceneGadget g) {
|
||||||
|
EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos);
|
||||||
|
|
||||||
|
if (entity.getGadgetData() == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setBlockId(blockId);
|
||||||
|
entity.setConfigId(g.config_id);
|
||||||
|
entity.setGroupId(groupId);
|
||||||
|
entity.getRotation().set(g.rot);
|
||||||
|
entity.setState(g.state);
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityMonster createMonster(int groupId, int blockId, SceneMonster monster) {
|
||||||
|
if(monster == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id);
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate level
|
||||||
|
int level = monster.level;
|
||||||
|
|
||||||
|
if (getScene().getDungeonData() != null) {
|
||||||
|
level = getScene().getDungeonData().getShowLevel();
|
||||||
|
} else if (getScene().getWorld().getWorldLevel() > 0) {
|
||||||
|
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getScene().getWorld().getWorldLevel());
|
||||||
|
|
||||||
|
if (worldLevelData != null) {
|
||||||
|
level = worldLevelData.getMonsterLevel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn mob
|
||||||
|
EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, level);
|
||||||
|
entity.getRotation().set(monster.rot);
|
||||||
|
entity.setGroupId(groupId);
|
||||||
|
entity.setBlockId(blockId);
|
||||||
|
entity.setConfigId(monster.config_id);
|
||||||
|
|
||||||
|
this.getScriptMonsterSpawnService()
|
||||||
|
.onMonsterCreatedListener.forEach(action -> action.onNotify(entity));
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntity(GameEntity gameEntity){
|
||||||
|
getScene().addEntity(gameEntity);
|
||||||
|
callCreateEvent(gameEntity);
|
||||||
|
}
|
||||||
|
public void meetEntities(List<? extends GameEntity> gameEntity){
|
||||||
|
getScene().addEntities(gameEntity, VisionTypeOuterClass.VisionType.VISION_MEET);
|
||||||
|
gameEntity.forEach(this::callCreateEvent);
|
||||||
|
}
|
||||||
|
public void addEntities(List<? extends GameEntity> gameEntity){
|
||||||
|
getScene().addEntities(gameEntity);
|
||||||
|
gameEntity.forEach(this::callCreateEvent);
|
||||||
|
}
|
||||||
|
public void callCreateEvent(GameEntity gameEntity){
|
||||||
|
if(!isInit){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(gameEntity instanceof EntityMonster entityMonster){
|
||||||
|
callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entityMonster.getConfigId()));
|
||||||
|
}
|
||||||
|
if(gameEntity instanceof EntityGadget entityGadget){
|
||||||
|
this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entityGadget.getConfigId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhTree<SceneBlock> getBlocksIndex() {
|
||||||
|
return meta.sceneBlockIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,6 +355,36 @@ public class ScriptLib {
|
|||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int CheckRemainGadgetCountByGroupId(LuaTable table){
|
||||||
|
logger.debug("[LUA] Call CheckRemainGadgetCountByGroupId with {}",
|
||||||
|
printTable(table));
|
||||||
|
var groupId = table.get("group_id").toint();
|
||||||
|
|
||||||
|
var count = getSceneScriptManager().getScene().getEntities().values().stream()
|
||||||
|
.filter(g -> g instanceof EntityGadget entityGadget && entityGadget.getGroupId() == groupId)
|
||||||
|
.count();
|
||||||
|
return (int)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetGadgetStateByConfigId(int groupId, int configId){
|
||||||
|
logger.debug("[LUA] Call GetGadgetStateByConfigId with {},{}",
|
||||||
|
groupId, configId);
|
||||||
|
var gadget = getSceneScriptManager().getScene().getEntities().values().stream()
|
||||||
|
.filter(g -> g instanceof EntityGadget entityGadget && entityGadget.getGroupId() == groupId)
|
||||||
|
.filter(g -> g.getConfigId() == configId)
|
||||||
|
.findFirst();
|
||||||
|
if(gadget.isEmpty()){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var stat = ((EntityGadget)gadget.get()).getState();
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
|
public int SetGadgetStateByConfigId(int configId, LuaTable gadgetState){
|
||||||
|
logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}",
|
||||||
|
configId, printTable(gadgetState));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
package emu.grasscutter.scripts;
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.script.Compilable;
|
|
||||||
import javax.script.CompiledScript;
|
|
||||||
import javax.script.ScriptEngine;
|
|
||||||
import javax.script.ScriptEngineFactory;
|
|
||||||
import javax.script.ScriptEngineManager;
|
|
||||||
|
|
||||||
import org.luaj.vm2.LuaTable;
|
|
||||||
import org.luaj.vm2.LuaValue;
|
|
||||||
import org.luaj.vm2.lib.OneArgFunction;
|
|
||||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
|
||||||
import org.luaj.vm2.script.LuajContext;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.props.EntityType;
|
import emu.grasscutter.game.props.EntityType;
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
import emu.grasscutter.scripts.constants.ScriptGadgetState;
|
import emu.grasscutter.scripts.constants.ScriptGadgetState;
|
||||||
import emu.grasscutter.scripts.constants.ScriptRegionShape;
|
import emu.grasscutter.scripts.constants.ScriptRegionShape;
|
||||||
|
import emu.grasscutter.scripts.data.SceneMeta;
|
||||||
import emu.grasscutter.scripts.serializer.LuaSerializer;
|
import emu.grasscutter.scripts.serializer.LuaSerializer;
|
||||||
import emu.grasscutter.scripts.serializer.Serializer;
|
import emu.grasscutter.scripts.serializer.Serializer;
|
||||||
|
import org.luaj.vm2.LuaTable;
|
||||||
|
import org.luaj.vm2.LuaValue;
|
||||||
|
import org.luaj.vm2.lib.OneArgFunction;
|
||||||
|
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||||
|
import org.luaj.vm2.script.LuajContext;
|
||||||
|
|
||||||
|
import javax.script.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class ScriptLoader {
|
public class ScriptLoader {
|
||||||
private static ScriptEngineManager sm;
|
private static ScriptEngineManager sm;
|
||||||
@ -34,8 +29,14 @@ public class ScriptLoader {
|
|||||||
private static ScriptEngineFactory factory;
|
private static ScriptEngineFactory factory;
|
||||||
private static String fileType;
|
private static String fileType;
|
||||||
private static Serializer serializer;
|
private static Serializer serializer;
|
||||||
|
/**
|
||||||
private static Map<String, CompiledScript> scripts = new HashMap<>();
|
* suggest GC to remove it if the memory is less
|
||||||
|
*/
|
||||||
|
private static Map<String, SoftReference<CompiledScript>> scriptsCache = new ConcurrentHashMap<>();
|
||||||
|
/**
|
||||||
|
* sceneId - SceneMeta
|
||||||
|
*/
|
||||||
|
private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public synchronized static void init() throws Exception {
|
public synchronized static void init() throws Exception {
|
||||||
if (sm != null) {
|
if (sm != null) {
|
||||||
@ -81,25 +82,42 @@ public class ScriptLoader {
|
|||||||
return serializer;
|
return serializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Optional<T> tryGet(SoftReference<T> softReference){
|
||||||
|
try{
|
||||||
|
return Optional.ofNullable(softReference.get());
|
||||||
|
}catch (NullPointerException npe){
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
public static CompiledScript getScriptByPath(String path) {
|
public static CompiledScript getScriptByPath(String path) {
|
||||||
CompiledScript sc = scripts.get(path);
|
var sc = tryGet(scriptsCache.get(path));
|
||||||
|
if (sc.isPresent()) {
|
||||||
Grasscutter.getLogger().info("Loaded " + path);
|
return sc.get();
|
||||||
|
}
|
||||||
if (sc == null) {
|
|
||||||
File file = new File(path);
|
Grasscutter.getLogger().info("Loaded Script" + path);
|
||||||
|
|
||||||
if (!file.exists()) return null;
|
File file = new File(path);
|
||||||
|
|
||||||
try (FileReader fr = new FileReader(file)) {
|
if (!file.exists()) return null;
|
||||||
sc = ((Compilable) getEngine()).compile(fr);
|
|
||||||
scripts.put(path, sc);
|
try (FileReader fr = new FileReader(file)) {
|
||||||
} catch (Exception e) {
|
var script = ((Compilable) getEngine()).compile(fr);
|
||||||
//e.printStackTrace();
|
scriptsCache.put(path, new SoftReference<>(script));
|
||||||
return null;
|
return script;
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().error("Loaded Script {} failed!", path, e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SceneMeta getSceneMeta(int sceneId) {
|
||||||
|
return tryGet(sceneMetaCache.get(sceneId)).orElseGet(() -> {
|
||||||
|
var instance = SceneMeta.of(sceneId);
|
||||||
|
sceneMetaCache.put(sceneId, new SoftReference<>(instance));
|
||||||
|
return instance;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,69 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import ch.ethz.globis.phtree.PhTree;
|
||||||
|
import ch.ethz.globis.phtree.v16.PhTree16;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.scripts.SceneIndexManager;
|
||||||
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
|
import javax.script.CompiledScript;
|
||||||
|
import javax.script.ScriptException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import emu.grasscutter.utils.Position;
|
import static emu.grasscutter.Configuration.SCRIPT;
|
||||||
|
|
||||||
public class SceneBlock {
|
public class SceneBlock {
|
||||||
public int id;
|
public int id;
|
||||||
public Position max;
|
public Position max;
|
||||||
public Position min;
|
public Position min;
|
||||||
|
|
||||||
|
public int sceneId;
|
||||||
public List<SceneGroup> groups;
|
public List<SceneGroup> groups;
|
||||||
|
public PhTree<SceneGroup> sceneGroupIndex = new PhTree16<>(3);
|
||||||
|
|
||||||
|
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||||
|
|
||||||
|
public boolean isLoaded() {
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoaded(boolean loaded) {
|
||||||
|
this.loaded = loaded;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean contains(Position pos) {
|
public boolean contains(Position pos) {
|
||||||
return pos.getX() <= max.getX() && pos.getX() >= min.getX() &&
|
return pos.getX() <= max.getX() && pos.getX() >= min.getX() &&
|
||||||
pos.getZ() <= max.getZ() && pos.getZ() >= min.getZ();
|
pos.getZ() <= max.getZ() && pos.getZ() >= min.getZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SceneBlock load(int sceneId, Bindings bindings){
|
||||||
|
if(loaded){
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.sceneId = sceneId;
|
||||||
|
setLoaded(true);
|
||||||
|
|
||||||
|
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||||
|
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + id + "." + ScriptLoader.getScriptType()));
|
||||||
|
|
||||||
|
if (cs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eval script
|
||||||
|
try {
|
||||||
|
cs.eval(bindings);
|
||||||
|
|
||||||
|
// Set groups
|
||||||
|
groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups"));
|
||||||
|
groups.forEach(g -> g.block_id = id);
|
||||||
|
SceneIndexManager.buildIndex(this.sceneGroupIndex, groups, g -> g.pos.toLongArray());
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading block " + id + " in scene " + sceneId, e);
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().info("scene {} block {} is loaded successfully.", sceneId, id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,12 +1,6 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
import emu.grasscutter.utils.Position;
|
public class SceneGadget extends SceneObject{
|
||||||
|
|
||||||
public class SceneGadget {
|
|
||||||
public int level;
|
|
||||||
public int config_id;
|
|
||||||
public int gadget_id;
|
public int gadget_id;
|
||||||
public int state;
|
public int state;
|
||||||
public Position pos;
|
|
||||||
public Position rot;
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
|
import javax.script.CompiledScript;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static emu.grasscutter.Configuration.SCRIPTS_FOLDER;
|
||||||
|
|
||||||
public class SceneGroup {
|
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
|
||||||
@ -22,17 +33,93 @@ public class SceneGroup {
|
|||||||
public List<SceneSuite> suites;
|
public List<SceneSuite> suites;
|
||||||
public SceneInitConfig init_config;
|
public SceneInitConfig init_config;
|
||||||
|
|
||||||
private transient boolean isLoaded; // Not an actual variable in the scripts either
|
public List<SceneVar> variables;
|
||||||
|
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||||
|
|
||||||
public boolean isLoaded() {
|
public boolean isLoaded() {
|
||||||
return isLoaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setLoaded(boolean loaded) {
|
public void setLoaded(boolean loaded) {
|
||||||
return loaded;
|
this.loaded = loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient CompiledScript script; // Not an actual variable in the scripts either
|
||||||
|
|
||||||
|
public CompiledScript getScript() {
|
||||||
|
return script;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SceneSuite getSuiteByIndex(int index) {
|
public SceneSuite getSuiteByIndex(int index) {
|
||||||
return suites.get(index - 1);
|
return suites.get(index - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SceneGroup load(int sceneId, Bindings bindings){
|
||||||
|
if(loaded){
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
// Set flag here so if there is no script, we dont call this function over and over again.
|
||||||
|
setLoaded(true);
|
||||||
|
|
||||||
|
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||||
|
SCRIPTS_FOLDER + "Scene/" + sceneId + "/scene" + sceneId + "_group" + id + "." + ScriptLoader.getScriptType());
|
||||||
|
|
||||||
|
if (cs == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.script = cs;
|
||||||
|
// Eval script
|
||||||
|
try {
|
||||||
|
cs.eval(bindings);
|
||||||
|
|
||||||
|
// Set
|
||||||
|
monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")).stream()
|
||||||
|
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
||||||
|
gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets"));
|
||||||
|
triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers"));
|
||||||
|
suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites"));
|
||||||
|
regions = ScriptLoader.getSerializer().toList(SceneRegion.class, bindings.get("regions"));
|
||||||
|
init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config"));
|
||||||
|
|
||||||
|
// Add variables to suite
|
||||||
|
variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables"));
|
||||||
|
|
||||||
|
|
||||||
|
// Add monsters to suite TODO optimize
|
||||||
|
Int2ObjectMap<Object> map = new Int2ObjectOpenHashMap<>();
|
||||||
|
monsters.entrySet().forEach(m -> map.put(m.getValue().config_id, m));
|
||||||
|
monsters.values().forEach(m -> m.groupId = id);
|
||||||
|
gadgets.forEach(m -> map.put(m.config_id, m));
|
||||||
|
gadgets.forEach(m -> m.groupId = id);
|
||||||
|
|
||||||
|
for (SceneSuite suite : suites) {
|
||||||
|
suite.sceneMonsters = new ArrayList<>(suite.monsters.size());
|
||||||
|
suite.monsters.forEach(id -> {
|
||||||
|
Object objEntry = map.get(id.intValue());
|
||||||
|
if (objEntry instanceof Map.Entry<?,?> monsterEntry) {
|
||||||
|
Object monster = monsterEntry.getValue();
|
||||||
|
if(monster instanceof SceneMonster sceneMonster){
|
||||||
|
suite.sceneMonsters.add(sceneMonster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.sceneGadgets = new ArrayList<>(suite.gadgets.size());
|
||||||
|
for (int id : suite.gadgets) {
|
||||||
|
try {
|
||||||
|
SceneGadget gadget = (SceneGadget) map.get(id);
|
||||||
|
if (gadget != null) {
|
||||||
|
suite.sceneGadgets.add(gadget);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading group " + id + " in scene " + sceneId, e);
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().info("group {} in scene {} is loaded successfully.", id, sceneId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
71
src/main/java/emu/grasscutter/scripts/data/SceneMeta.java
Normal file
71
src/main/java/emu/grasscutter/scripts/data/SceneMeta.java
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import ch.ethz.globis.phtree.PhTree;
|
||||||
|
import ch.ethz.globis.phtree.v16.PhTree16;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.scripts.SceneIndexManager;
|
||||||
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
|
import javax.script.CompiledScript;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static emu.grasscutter.Configuration.SCRIPT;
|
||||||
|
|
||||||
|
public class SceneMeta {
|
||||||
|
|
||||||
|
public SceneConfig config;
|
||||||
|
public Map<Integer, SceneBlock> blocks;
|
||||||
|
|
||||||
|
public Bindings context;
|
||||||
|
|
||||||
|
public PhTree<SceneBlock> sceneBlockIndex = new PhTree16<>(2);
|
||||||
|
|
||||||
|
public static SceneMeta of(int sceneId) {
|
||||||
|
return new SceneMeta().load(sceneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SceneMeta load(int sceneId){
|
||||||
|
// Get compiled script if cached
|
||||||
|
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||||
|
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "." + ScriptLoader.getScriptType()));
|
||||||
|
|
||||||
|
if (cs == null) {
|
||||||
|
Grasscutter.getLogger().warn("No script found for scene " + sceneId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create bindings
|
||||||
|
context = ScriptLoader.getEngine().createBindings();
|
||||||
|
|
||||||
|
// Eval script
|
||||||
|
try {
|
||||||
|
cs.eval(context);
|
||||||
|
|
||||||
|
this.config = ScriptLoader.getSerializer().toObject(SceneConfig.class, context.get("scene_config"));
|
||||||
|
|
||||||
|
// TODO optimize later
|
||||||
|
// Create blocks
|
||||||
|
List<Integer> blockIds = ScriptLoader.getSerializer().toList(Integer.class, context.get("blocks"));
|
||||||
|
List<SceneBlock> blocks = ScriptLoader.getSerializer().toList(SceneBlock.class, context.get("block_rects"));
|
||||||
|
|
||||||
|
for (int i = 0; i < blocks.size(); i++) {
|
||||||
|
SceneBlock block = blocks.get(i);
|
||||||
|
block.id = blockIds.get(i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blocks = blocks.stream().collect(Collectors.toMap(b -> b.id, b -> b));
|
||||||
|
SceneIndexManager.buildIndex(this.sceneBlockIndex, blocks, g -> g.min.toXZLongArray());
|
||||||
|
SceneIndexManager.buildIndex(this.sceneBlockIndex, blocks, g -> g.max.toXZLongArray());
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
Grasscutter.getLogger().error("Error running script", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().info("scene {} metadata is loaded successfully.", sceneId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,5 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
import emu.grasscutter.utils.Position;
|
public class SceneMonster extends SceneObject{
|
||||||
|
|
||||||
public class SceneMonster {
|
|
||||||
public int level;
|
|
||||||
public int config_id;
|
|
||||||
public int monster_id;
|
public int monster_id;
|
||||||
public Position pos;
|
|
||||||
public Position rot;
|
|
||||||
}
|
}
|
15
src/main/java/emu/grasscutter/scripts/data/SceneObject.java
Normal file
15
src/main/java/emu/grasscutter/scripts/data/SceneObject.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
|
public class SceneObject {
|
||||||
|
public int level;
|
||||||
|
public int config_id;
|
||||||
|
|
||||||
|
public Position pos;
|
||||||
|
public Position rot;
|
||||||
|
/**
|
||||||
|
* not set by lua
|
||||||
|
*/
|
||||||
|
public int groupId;
|
||||||
|
}
|
@ -76,7 +76,10 @@ public class LuaSerializer implements Serializer {
|
|||||||
LuaValue[] keys = table.keys();
|
LuaValue[] keys = table.keys();
|
||||||
for (LuaValue k : keys) {
|
for (LuaValue k : keys) {
|
||||||
try {
|
try {
|
||||||
Field field = object.getClass().getDeclaredField(k.checkjstring());
|
Field field = getField(object.getClass(), k.checkjstring());
|
||||||
|
if (field == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
LuaValue keyValue = table.get(k);
|
LuaValue keyValue = table.get(k);
|
||||||
@ -103,4 +106,17 @@ public class LuaSerializer implements Serializer {
|
|||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> Field getField(Class<T> clazz, String name){
|
||||||
|
try{
|
||||||
|
return clazz.getField(name);
|
||||||
|
} catch (NoSuchFieldException ex) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(name);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// ignore
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,9 @@ import java.util.List;
|
|||||||
public class ScriptMonsterSpawnService {
|
public class ScriptMonsterSpawnService {
|
||||||
|
|
||||||
private final SceneScriptManager sceneScriptManager;
|
private final SceneScriptManager sceneScriptManager;
|
||||||
private final List<ScriptMonsterListener> onMonsterCreatedListener = new ArrayList<>();
|
public final List<ScriptMonsterListener> onMonsterCreatedListener = new ArrayList<>();
|
||||||
|
|
||||||
private final List<ScriptMonsterListener> onMonsterDeadListener = new ArrayList<>();
|
public final List<ScriptMonsterListener> onMonsterDeadListener = new ArrayList<>();
|
||||||
|
|
||||||
public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){
|
public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){
|
||||||
this.sceneScriptManager = sceneScriptManager;
|
this.sceneScriptManager = sceneScriptManager;
|
||||||
@ -39,40 +39,5 @@ public class ScriptMonsterSpawnService {
|
|||||||
public void onMonsterDead(EntityMonster entityMonster){
|
public void onMonsterDead(EntityMonster entityMonster){
|
||||||
onMonsterDeadListener.forEach(l -> l.onNotify(entityMonster));
|
onMonsterDeadListener.forEach(l -> l.onNotify(entityMonster));
|
||||||
}
|
}
|
||||||
public void spawnMonster(int groupId, SceneMonster monster) {
|
|
||||||
if(monster == null){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id);
|
|
||||||
|
|
||||||
if (data == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate level
|
|
||||||
int level = monster.level;
|
|
||||||
|
|
||||||
if (sceneScriptManager.getScene().getDungeonData() != null) {
|
|
||||||
level = sceneScriptManager.getScene().getDungeonData().getShowLevel();
|
|
||||||
} else if (sceneScriptManager.getScene().getWorld().getWorldLevel() > 0) {
|
|
||||||
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(sceneScriptManager.getScene().getWorld().getWorldLevel());
|
|
||||||
|
|
||||||
if (worldLevelData != null) {
|
|
||||||
level = worldLevelData.getMonsterLevel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawn mob
|
|
||||||
EntityMonster entity = new EntityMonster(sceneScriptManager.getScene(), data, monster.pos, level);
|
|
||||||
entity.getRotation().set(monster.rot);
|
|
||||||
entity.setGroupId(groupId);
|
|
||||||
entity.setConfigId(monster.config_id);
|
|
||||||
|
|
||||||
onMonsterCreatedListener.forEach(action -> action.onNotify(entity));
|
|
||||||
|
|
||||||
sceneScriptManager.getScene().addEntity(entity);
|
|
||||||
|
|
||||||
sceneScriptManager.callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ public class ScriptMonsterTideService {
|
|||||||
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead);
|
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead);
|
||||||
// spawn the first turn
|
// spawn the first turn
|
||||||
for (int i = 0; i < this.monsterSceneLimit; i++) {
|
for (int i = 0; i < this.monsterSceneLimit; i++) {
|
||||||
this.sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(group.id, getNextMonster());
|
sceneScriptManager.addEntity(this.sceneScriptManager.createMonster(group.id, group.block_id, getNextMonster()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ public class ScriptMonsterTideService {
|
|||||||
monsterKillCount.incrementAndGet();
|
monsterKillCount.incrementAndGet();
|
||||||
if (monsterTideCount.get() > 0) {
|
if (monsterTideCount.get() > 0) {
|
||||||
// add more
|
// add more
|
||||||
sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(currentGroup.id, getNextMonster());
|
sceneScriptManager.addEntity(sceneScriptManager.createMonster(currentGroup.id, currentGroup.block_id, getNextMonster()));
|
||||||
}
|
}
|
||||||
// spawn the last turn of monsters
|
// spawn the last turn of monsters
|
||||||
// fix the 5-2
|
// fix the 5-2
|
||||||
|
@ -57,7 +57,6 @@ public final class GameServer extends KcpServer {
|
|||||||
|
|
||||||
private final CombineManger combineManger;
|
private final CombineManger combineManger;
|
||||||
private final TowerScheduleManager towerScheduleManager;
|
private final TowerScheduleManager towerScheduleManager;
|
||||||
|
|
||||||
public GameServer() {
|
public GameServer() {
|
||||||
this(new InetSocketAddress(
|
this(new InetSocketAddress(
|
||||||
GAME_INFO.bindAddress,
|
GAME_INFO.bindAddress,
|
||||||
@ -206,7 +205,7 @@ public final class GameServer extends KcpServer {
|
|||||||
return DatabaseHelper.getAccountByName(username);
|
return DatabaseHelper.getAccountByName(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTick() throws Exception {
|
public synchronized void onTick(){
|
||||||
Iterator<World> it = this.getWorlds().iterator();
|
Iterator<World> it = this.getWorlds().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
World world = it.next();
|
World world = it.next();
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
package emu.grasscutter.server.game;
|
package emu.grasscutter.server.game;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
@ -21,6 +16,11 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
import static emu.grasscutter.Configuration.*;
|
import static emu.grasscutter.Configuration.*;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ public class PacketSceneEntityAppearNotify extends BasePacket {
|
|||||||
this(player.getTeamManager().getCurrentAvatarEntity());
|
this(player.getTeamManager().getCurrentAvatarEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketSceneEntityAppearNotify(Collection<GameEntity> entities, VisionType visionType) {
|
public PacketSceneEntityAppearNotify(Collection<? extends GameEntity> entities, VisionType visionType) {
|
||||||
super(PacketOpcodes.SceneEntityAppearNotify, true);
|
super(PacketOpcodes.SceneEntityAppearNotify, true);
|
||||||
|
|
||||||
SceneEntityAppearNotify.Builder proto = SceneEntityAppearNotify.newBuilder()
|
SceneEntityAppearNotify.Builder proto = SceneEntityAppearNotify.newBuilder()
|
||||||
|
@ -136,6 +136,7 @@ public class ConfigContainer {
|
|||||||
public int bindPort = 22102;
|
public int bindPort = 22102;
|
||||||
/* This is the port used in the default region. */
|
/* This is the port used in the default region. */
|
||||||
public int accessPort = 0;
|
public int accessPort = 0;
|
||||||
|
public boolean enableScriptInBigWorld = false;
|
||||||
|
|
||||||
public GameOptions gameOptions = new GameOptions();
|
public GameOptions gameOptions = new GameOptions();
|
||||||
public JoinOptions joinOptions = new JoinOptions();
|
public JoinOptions joinOptions = new JoinOptions();
|
||||||
|
@ -155,4 +155,10 @@ public class Position implements Serializable {
|
|||||||
.setZ(this.getZ())
|
.setZ(this.getZ())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
public long[] toLongArray(){
|
||||||
|
return new long[]{(long) x, (long) y, (long) z};
|
||||||
|
}
|
||||||
|
public long[] toXZLongArray(){
|
||||||
|
return new long[]{(long) x, (long) z};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user