Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add events to support scene group substitution #2413

Merged
merged 3 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}
Loading