mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-21 03:15:59 +01: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:
@@ -1,5 +1,6 @@
|
||||
package emu.grasscutter.game.world;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameDepot;
|
||||
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.proto.AttackResultOuterClass.AttackResult;
|
||||
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
|
||||
import emu.grasscutter.scripts.SceneIndexManager;
|
||||
import emu.grasscutter.scripts.SceneScriptManager;
|
||||
import emu.grasscutter.scripts.data.SceneBlock;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify;
|
||||
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.PacketSceneEntityAppearNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
|
||||
@@ -34,6 +35,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.danilopianini.util.SpatialIndex;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Scene {
|
||||
private final World world;
|
||||
@@ -57,7 +59,6 @@ public class Scene {
|
||||
private DungeonData dungeonData;
|
||||
private int prevScene; // Id of the previous scene
|
||||
private int prevScenePoint;
|
||||
|
||||
public Scene(World world, SceneData sceneData) {
|
||||
this.world = world;
|
||||
this.sceneData = sceneData;
|
||||
@@ -323,14 +324,21 @@ public class Scene {
|
||||
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_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) {
|
||||
this.addEntityDirectly(entity);
|
||||
}
|
||||
|
||||
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_BORN));
|
||||
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, visionType));
|
||||
}
|
||||
|
||||
private GameEntity removeEntityDirectly(GameEntity entity) {
|
||||
@@ -354,7 +362,7 @@ public class Scene {
|
||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VISION_REPLACE));
|
||||
this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VISION_REPLACE, oldEntity.getId()));
|
||||
}
|
||||
|
||||
|
||||
public void showOtherEntities(Player player) {
|
||||
List<GameEntity> entities = new LinkedList<>();
|
||||
GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity();
|
||||
@@ -402,7 +410,7 @@ public class Scene {
|
||||
// Death event
|
||||
target.onDeath(attackerId);
|
||||
}
|
||||
|
||||
|
||||
public void onTick() {
|
||||
if (this.getScriptManager().isInit()) {
|
||||
this.checkBlocks();
|
||||
@@ -410,9 +418,8 @@ public class Scene {
|
||||
// TEMPORARY
|
||||
this.checkSpawns();
|
||||
}
|
||||
|
||||
// Triggers
|
||||
this.getScriptManager().onTick();
|
||||
this.scriptManager.checkRegions();
|
||||
}
|
||||
|
||||
// TODO - Test
|
||||
@@ -484,54 +491,75 @@ public class Scene {
|
||||
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() {
|
||||
Set<SceneBlock> visible = new HashSet<>();
|
||||
|
||||
for (Player player : this.getPlayers()) {
|
||||
for (SceneBlock block : getScriptManager().getBlocks()) {
|
||||
if (!block.contains(player.getPos())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visible.add(block);
|
||||
}
|
||||
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 (SceneBlock block : visible) {
|
||||
|
||||
for(var block : visible){
|
||||
if (!this.getLoadedBlocks().contains(block)) {
|
||||
this.onLoadBlock(block);
|
||||
this.onLoadBlock(block, this.getPlayers());
|
||||
this.getLoadedBlocks().add(block);
|
||||
}
|
||||
this.getPlayers().stream()
|
||||
.filter(p -> block.contains(p.getPos()))
|
||||
.forEach(p -> playerMeetGroups(p, block));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
public void onLoadBlock(SceneBlock block) {
|
||||
for (SceneGroup group : block.groups) {
|
||||
public List<SceneGroup> playerMeetGroups(Player player, SceneBlock block){
|
||||
int RANGE = 100;
|
||||
|
||||
var sceneGroups = SceneIndexManager.queryNeighbors(block.sceneGroupIndex, player.getPos(), RANGE);
|
||||
|
||||
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
|
||||
if (!group.isLoaded()) {
|
||||
this.getScriptManager().loadGroupFromScript(group);
|
||||
}
|
||||
|
||||
group.triggers.forEach(getScriptManager()::registerTrigger);
|
||||
group.regions.forEach(getScriptManager()::registerRegion);
|
||||
this.getScriptManager().loadGroupFromScript(group);
|
||||
}
|
||||
|
||||
// Spawn gadgets AFTER triggers are added
|
||||
// TODO
|
||||
for (SceneGroup group : block.groups) {
|
||||
for (SceneGroup group : groups) {
|
||||
if (group.init_config == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -541,26 +569,36 @@ public class Scene {
|
||||
if (suite == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
this.getScriptManager().spawnGadgetsInGroup(group, suite);
|
||||
var suiteData = group.getSuiteByIndex(suite);
|
||||
getScriptManager().spawnGadgetsInGroup(group,suiteData);
|
||||
getScriptManager().spawnMonstersInGroup(group, suiteData);
|
||||
suite++;
|
||||
} while (suite < group.init_config.end_suite);
|
||||
}
|
||||
Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id);
|
||||
}
|
||||
|
||||
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) {
|
||||
toRemove.stream().forEach(this::removeEntityDirectly);
|
||||
toRemove.forEach(this::removeEntityDirectly);
|
||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE));
|
||||
}
|
||||
|
||||
for (SceneGroup group : block.groups) {
|
||||
group.triggers.forEach(getScriptManager()::deregisterTrigger);
|
||||
group.regions.forEach(getScriptManager()::deregisterRegion);
|
||||
if(group.triggers != null){
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user