Skip to content

Commit

Permalink
Add events to support scene group substitution (#2413)
Browse files Browse the repository at this point in the history
* Add events to support scene group substitution

* make event members private with getter/setter

* delete stray unused var
  • Loading branch information
longfruit authored Nov 1, 2023
1 parent 269f7b4 commit cf6fb27
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 29 deletions.
16 changes: 10 additions & 6 deletions src/main/java/emu/grasscutter/game/entity/EntityMonster.java
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,16 @@ public void onDeath(int killerId) {
if (scriptManager.isInit() && this.getGroupId() > 0) {
Optional.ofNullable(scriptManager.getScriptMonsterSpawnService()).ifPresent(s -> s.onMonsterDead(this));

// prevent spawn monster after success
/*if (challenge.map(c -> c.inProgress()).orElse(true)) {
scriptManager.callEvent(new ScriptArgs(EventType.EVENT_ANY_MONSTER_DIE, this.getConfigId()).setGroupId(this.getGroupId()));
} else if (getScene().getChallenge() == null) {
}*/
scriptManager.callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_MONSTER_DIE, this.getConfigId()));
// Ensure each EVENT_ANY_MONSTER_DIE runs to completion.
// Multiple such events firing at the same time may cause
// the same lua trigger to fire multiple times, when it
// should happen only once.
var future = scriptManager.callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_MONSTER_DIE, this.getConfigId()));
try {
future.get();
} catch (Exception e) {
e.printStackTrace();
}
}
// Battle Pass trigger
scene.getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@

@Data
public class GroupReplacementData {
int id;
List<Integer> replace_groups;
public int id;
public List<Integer> replace_groups;

public GroupReplacementData() {}

public GroupReplacementData(int id, List<Integer> replace_groups) {
this.id = id;
this.replace_groups = replace_groups;
}
}
3 changes: 3 additions & 0 deletions src/main/java/emu/grasscutter/game/world/Scene.java
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,9 @@ public void unloadGroup(SceneBlock block, int group_id) {
if (group.regions != null) {
group.regions.values().forEach(getScriptManager()::deregisterRegion);
}
if (challenge != null && group.id == challenge.getGroup().id) {
challenge.fail();
}

scriptManager.getLoadedGroupSetPerBlock().get(block.id).remove(group);
this.loadedGroups.remove(group);
Expand Down
39 changes: 27 additions & 12 deletions src/main/java/emu/grasscutter/scripts/SceneScriptManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.*;
import emu.grasscutter.scripts.service.*;
import emu.grasscutter.server.event.game.SceneMetaLoadEvent;
import emu.grasscutter.server.packet.send.PacketGroupSuiteNotify;
import emu.grasscutter.utils.*;
import io.netty.util.concurrent.FastThreadLocalThread;
Expand All @@ -38,6 +39,7 @@ public class SceneScriptManager {
private final Map<String, Integer> variables;
private SceneMeta meta;
private boolean isInit;
private boolean noCacheGroupGridsToDisk;

private final Map<String, SceneTimeAxis> timeAxis = new ConcurrentHashMap<>();

Expand Down Expand Up @@ -134,7 +136,7 @@ public void registerTrigger(List<SceneTrigger> triggers) {
public void registerTrigger(SceneTrigger trigger) {
this.triggerInvocations.put(trigger.getName(), new AtomicInteger(0));
this.getTriggersByEvent(trigger.getEvent()).add(trigger);
Grasscutter.getLogger().trace("Registered trigger {}", trigger.getName());
Grasscutter.getLogger().trace("Registered trigger {} from group {}", trigger.getName(), trigger.getCurrentGroup().id);
}

public void deregisterTrigger(List<SceneTrigger> triggers) {
Expand All @@ -143,7 +145,7 @@ public void deregisterTrigger(List<SceneTrigger> triggers) {

public void deregisterTrigger(SceneTrigger trigger) {
this.getTriggersByEvent(trigger.getEvent()).remove(trigger);
Grasscutter.getLogger().trace("deregistered trigger {}", trigger.getName());
Grasscutter.getLogger().trace("deregistered trigger {} from group {}", trigger.getName(), trigger.getCurrentGroup().id);
}

public void resetTriggers(int eventId) {
Expand Down Expand Up @@ -438,6 +440,17 @@ private static int getGadgetVisionLevel(int gadget_id) {
}

private void init() {
var event = new SceneMetaLoadEvent(getScene());
event.call();

if (event.isOverride()) {
// Group grids should not be cached to disk when a scene
// group override is in effect. Otherwise, when the server
// next runs without that override, the cached content
// will not make sense.
noCacheGroupGridsToDisk = true;
}

var meta = ScriptLoader.getSceneMeta(getScene().getId());
if (meta == null) {
return;
Expand All @@ -455,7 +468,7 @@ public List<Grid> getGroupGrids() {
return groupGridsCache.get(sceneId);
} else {
var path = FileUtils.getCachePath("scene" + sceneId + "_grid.json");
if (path.toFile().isFile() && !Grasscutter.config.server.game.cacheSceneEntitiesEveryRun) {
if (path.toFile().isFile() && !Grasscutter.config.server.game.cacheSceneEntitiesEveryRun && !noCacheGroupGridsToDisk) {
try {
var groupGrids = JsonUtils.loadToList(path, Grid.class);
groupGridsCache.put(sceneId, groupGrids);
Expand Down Expand Up @@ -585,15 +598,17 @@ public List<Grid> getGroupGrids() {
}
groupGridsCache.put(scene.getId(), groupGrids);

try {
Files.createDirectories(path.getParent());
} catch (IOException ignored) {
}
try (var file = new FileWriter(path.toFile())) {
file.write(JsonUtils.encode(groupGrids));
Grasscutter.getLogger().info("Scene {} saved grid file.", getScene().getId());
} catch (Exception e) {
Grasscutter.getLogger().error("Scene {} unable to save grid file.", getScene().getId(), e);
if (!noCacheGroupGridsToDisk) {
try {
Files.createDirectories(path.getParent());
} catch (IOException ignored) {
}
try (var file = new FileWriter(path.toFile())) {
file.write(JsonUtils.encode(groupGrids));
Grasscutter.getLogger().info("Scene {} saved grid file.", getScene().getId());
} catch (Exception e) {
Grasscutter.getLogger().error("Scene {} unable to save grid file.", getScene().getId(), e);
}
}
return groupGrids;
}
Expand Down
42 changes: 35 additions & 7 deletions src/main/java/emu/grasscutter/scripts/ScriptLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import emu.grasscutter.utils.FileUtils;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.nio.file.Files;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
Expand Down Expand Up @@ -172,15 +172,29 @@ public LuaValue call(LuaValue arg) {
* @return The sources of the script.
*/
public static String readScript(String path) {
return readScript(path, false);
}

/**
* Loads the sources of a script.
*
* @param path The path of the script.
* @param useAbsPath Use path as-is; don't look under Scripts resources.
* @return The sources of the script.
*/
public static String readScript(String path, boolean useAbsPath) {
// Check if the path is cached.
var cached = ScriptLoader.tryGet(ScriptLoader.scriptSources.get(path));
if (cached.isPresent()) {
return cached.get();
}

// Attempt to load the script.
var scriptPath = FileUtils.getScriptPath(path);
if (!Files.exists(scriptPath)) return null;
var scriptPath = useAbsPath ? Paths.get(path) : FileUtils.getScriptPath(path);
if (!Files.exists(scriptPath)) {
Grasscutter.getLogger().error("Could not find script at path {}", path);
return null;
}

try {
var source = Files.readString(scriptPath);
Expand All @@ -201,6 +215,17 @@ public static String readScript(String path) {
* @return The compiled script.
*/
public static CompiledScript getScript(String path) {
return getScript(path, false);
}

/**
* Fetches a script and compiles it, or uses the cached varient.
*
* @param path The path of the script.
* @param useAbsPath Use path as-is; don't look under Scripts resources.
* @return The compiled script.
*/
public static CompiledScript getScript(String path, boolean useAbsPath) {
// Check if the script is cached.
var sc = ScriptLoader.tryGet(ScriptLoader.scriptsCache.get(path));
if (sc.isPresent()) {
Expand All @@ -211,15 +236,18 @@ public static CompiledScript getScript(String path) {
CompiledScript script;
if (Configuration.FAST_REQUIRE) {
// Attempt to load the script.
var scriptPath = FileUtils.getScriptPath(path);
if (!Files.exists(scriptPath)) return null;
var scriptPath = useAbsPath ? Paths.get(path) : FileUtils.getScriptPath(path);
if (!Files.exists(scriptPath)) {
Grasscutter.getLogger().error("Could not find script at path {}", path);
return null;
}

// Compile the script from the file.
var source = Files.newBufferedReader(scriptPath);
script = ScriptLoader.getEngine().compile(source);
} else {
// Load the script sources.
var sources = ScriptLoader.readScript(path);
var sources = ScriptLoader.readScript(path, useAbsPath);
if (sources == null) return null;

// Check to see if the script references other scripts.
Expand All @@ -237,7 +265,7 @@ public static CompiledScript getScript(String path) {
var scriptName = line.substring(9, line.length() - 1);
// Resolve the script path.
var scriptPath = "Common/" + scriptName + ".lua";
var scriptSource = ScriptLoader.readScript(scriptPath);
var scriptSource = ScriptLoader.readScript(scriptPath, useAbsPath);
if (scriptSource == null) continue;

// Append the script source.
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/emu/grasscutter/scripts/data/SceneBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.scripts.*;
import emu.grasscutter.server.event.game.SceneBlockLoadedEvent;
import java.util.Map;
import java.util.stream.Collectors;
import javax.script.*;
Expand Down Expand Up @@ -64,6 +65,10 @@ public SceneBlock load(int sceneId, Bindings bindings) {
.collect(Collectors.toMap(x -> x.id, y -> y, (a, b) -> a));

this.groups.values().forEach(g -> g.block_id = this.id);

var event = new SceneBlockLoadedEvent(this);
event.call();

this.sceneGroupIndex =
SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
} catch (ScriptException exception) {
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/emu/grasscutter/scripts/data/SceneGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.scripts.ScriptLoader;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import javax.script.*;
Expand Down Expand Up @@ -39,6 +40,7 @@ public final class SceneGroup {
private transient boolean loaded;
private transient CompiledScript script;
private transient Bindings bindings;
public String overrideScriptPath;

public static SceneGroup of(int groupId) {
var group = new SceneGroup();
Expand Down Expand Up @@ -86,8 +88,12 @@ public synchronized SceneGroup load(int sceneId) {
// Create the bindings.
this.bindings = ScriptLoader.getEngine().createBindings();

var cs =
ScriptLoader.getScript("Scene/%s/scene%s_group%s.lua".formatted(sceneId, sceneId, this.id));
CompiledScript cs;
if (overrideScriptPath != null && !overrideScriptPath.equals("")) {
cs = ScriptLoader.getScript(overrideScriptPath, true);
} else {
cs = ScriptLoader.getScript("Scene/%s/scene%s_group%s.lua".formatted(sceneId, sceneId, this.id));
}

if (cs == null) {
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package emu.grasscutter.server.event.game;

import emu.grasscutter.server.event.types.ServerEvent;
import emu.grasscutter.scripts.data.SceneBlock;
import lombok.*;

@Getter
public final class SceneBlockLoadedEvent extends ServerEvent {
private SceneBlock block;

public SceneBlockLoadedEvent(SceneBlock block) {
super(Type.GAME);

this.block = block;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package emu.grasscutter.server.event.game;

import emu.grasscutter.game.world.Scene;
import emu.grasscutter.server.event.types.ServerEvent;
import lombok.*;

@Getter
public final class SceneMetaLoadEvent extends ServerEvent {
private Scene scene;
@Setter private boolean override;

public SceneMetaLoadEvent(Scene scene) {
super(Type.GAME);

this.scene = scene;
}
}

0 comments on commit cf6fb27

Please sign in to comment.