Compare commits

...

6 Commits

Author SHA1 Message Date
github-actions
b087e1fb4f Format code [skip actions] 2024-10-03 15:31:53 +00:00
KingRainbow44
d11e13a005
fix(GameConstants.java): Remove extra period in VERSION 2024-10-03 11:30:43 -04:00
KingRainbow44
10423dbada
fix(ActionServerLuaCall.java): Handle Lua function parameter passing 2024-09-28 20:18:22 -04:00
KingRainbow44
5b9d386e17
fix(AbilityModifier.java): Always parse param[1-3] as DynamicFloats 2024-09-28 19:51:29 -04:00
KingRainbow44
5167253136
fix(5.0): Update GameConstants.java with 5.0 changes 2024-09-28 18:48:51 -04:00
KingRainbow44
3c8d1263e5
fix: Re-add handbook_auth.html
its still used by handbook authenticator; i won't judge anyone if they're still loading the handbook
2024-09-28 18:47:43 -04:00
30 changed files with 172 additions and 39 deletions

View File

@ -7,8 +7,8 @@ import emu.grasscutter.utils.objects.SparseSet;
import java.util.Arrays; import java.util.Arrays;
public final class GameConstants { public final class GameConstants {
public static String VERSION = "4.0.0"; public static String VERSION = "5.0.0";
public static int[] VERSION_PARTS = {4, 0, 0}; public static int[] VERSION_PARTS = {5, 0, 0};
public static boolean DEBUG = false; public static boolean DEBUG = false;
public static final int DEFAULT_TEAMS = 4; public static final int DEFAULT_TEAMS = 4;
@ -16,7 +16,7 @@ public final class GameConstants {
public static final int MAIN_CHARACTER_MALE = 10000005; public static final int MAIN_CHARACTER_MALE = 10000005;
public static final int MAIN_CHARACTER_FEMALE = 10000007; public static final int MAIN_CHARACTER_FEMALE = 10000007;
public static final Position START_POSITION = new Position(2747, 194, -1719); public static final Position START_POSITION = new Position(2747, 194, -1719);
public static final int MAX_FRIENDS = 60; public static final int MAX_FRIENDS = 100;
public static final int MAX_FRIEND_REQUESTS = 50; public static final int MAX_FRIEND_REQUESTS = 50;
public static final int SERVER_CONSOLE_UID = 99; // The UID of the server console's "player". public static final int SERVER_CONSOLE_UID = 99; // The UID of the server console's "player".
public static final int BATTLE_PASS_MAX_LEVEL = 50; public static final int BATTLE_PASS_MAX_LEVEL = 50;

View File

@ -150,7 +150,6 @@ public final class Grasscutter {
httpServer.addRouter(AuthenticationHandler.class); httpServer.addRouter(AuthenticationHandler.class);
httpServer.addRouter(GachaHandler.class); httpServer.addRouter(GachaHandler.class);
httpServer.addRouter(DocumentationServerHandler.class); httpServer.addRouter(DocumentationServerHandler.class);
httpServer.addRouter(HandbookHandler.class);
} }
// Check if the HTTP server should start. // Check if the HTTP server should start.

View File

@ -345,9 +345,10 @@ public class AbilityModifier implements Serializable {
public DynamicFloat valueRangeMax; public DynamicFloat valueRangeMax;
public String overrideMapKey; public String overrideMapKey;
public int param1; public int paramNum;
public int param2; public DynamicFloat param1 = DynamicFloat.ZERO,
public int param3; param2 = DynamicFloat.ZERO,
param3 = DynamicFloat.ZERO;
public String funcName; public String funcName;
public LuaCallType luaCallType; public LuaCallType luaCallType;
@ -358,12 +359,18 @@ public class AbilityModifier implements Serializable {
public String content; public String content;
public enum LuaCallType { public enum LuaCallType {
Gadget,
@SerializedName(value = "OwnerGadegt", alternate = "OwnerGadget")
OwnerGadget,
FromGroup, FromGroup,
CurGalleryControlGroup, OwnerFromGroup,
CurChallengeGroup,
SpecificGroup, SpecificGroup,
CurScenePlay,
CurChallengeGroup,
CurRogueBossGroup,
CurGalleryControlGroup,
AbilityGroupSourceGroup, AbilityGroupSourceGroup,
CurScenePlay LevelBankZoneContainsGroup
} }
public enum DropType { public enum DropType {

View File

@ -6,6 +6,7 @@ import lombok.Data;
@Data @Data
public class ConfigGlobalCombat { public class ConfigGlobalCombat {
private DefaultAbilities defaultAbilities; private DefaultAbilities defaultAbilities;
// TODO: Add more indices // TODO: Add more indices
@Data @Data

View File

@ -13,6 +13,7 @@ public class RoutePoint {
private float waitTime; // optional private float waitTime; // optional
private float targetVelocity; // optional private float targetVelocity; // optional
private boolean hasReachEvent; // optional private boolean hasReachEvent; // optional
// rotRoundReachDir //optional Pos with optional values // rotRoundReachDir //optional Pos with optional values
// rotRoundLeaveDir //optional Pos with optional values // rotRoundLeaveDir //optional Pos with optional values

View File

@ -57,6 +57,10 @@ public class DynamicFloat {
return this.get(ability.getAbilitySpecials(), 0f); return this.get(ability.getAbilitySpecials(), 0f);
} }
public int getInt(Ability ability) {
return (int) this.get(ability.getAbilitySpecials(), 0f);
}
public float get(Object2FloatMap<String> props, float defaultValue) { public float get(Object2FloatMap<String> props, float defaultValue) {
if (!this.dynamic) return constant; if (!this.dynamic) return constant;

View File

@ -9,6 +9,7 @@ import lombok.Getter;
public class BlossomRefreshExcelConfigData extends GameResource { public class BlossomRefreshExcelConfigData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
// Map details // Map details
private long nameTextMapHash; private long nameTextMapHash;
private long descTextMapHash; private long descTextMapHash;

View File

@ -21,6 +21,7 @@ public class CoopChapterData extends GameResource {
// int avatarSortId; // int avatarSortId;
// String chapterIcon; // String chapterIcon;
List<CoopCondition> unlockCond; List<CoopCondition> unlockCond;
// int [] unlockCondTips; // int [] unlockCondTips;
// int openMaterialId; // int openMaterialId;
// int openMaterialNum; // int openMaterialNum;

View File

@ -65,7 +65,9 @@ public class Ability {
data.modifiers.values().stream() data.modifiers.values().stream()
.map( .map(
m -> m ->
m.onAdded == null ? Collections.<AbilityModifierAction>emptyList() : Arrays.asList(m.onAdded)) m.onAdded == null
? Collections.<AbilityModifierAction>emptyList()
: Arrays.asList(m.onAdded))
.flatMap(List::stream) .flatMap(List::stream)
.filter(action -> action.type == AbilityModifierAction.Type.AvatarSkillStart) .filter(action -> action.type == AbilityModifierAction.Type.AvatarSkillStart)
.map(action -> action.skillID) .map(action -> action.skillID)

View File

@ -15,9 +15,11 @@ public class ActionExecuteGadgetLua extends AbilityActionHandler {
// Investigate if we need to use target // Investigate if we need to use target
if (owner.getEntityController() != null) { if (owner.getEntityController() != null) {
owner var param1 = action.param1.getInt(ability);
.getEntityController() var param2 = action.param2.getInt(ability);
.onClientExecuteRequest(owner, action.param1, action.param2, action.param3); var param3 = action.param3.getInt(ability);
owner.getEntityController().onClientExecuteRequest(owner, param1, param2, param3);
return true; return true;
} }

View File

@ -8,6 +8,7 @@ import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.scripts.ScriptLoader; import emu.grasscutter.scripts.ScriptLoader;
import javax.script.Bindings; import javax.script.Bindings;
import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
@AbilityAction(AbilityModifierAction.Type.ServerLuaCall) @AbilityAction(AbilityModifierAction.Type.ServerLuaCall)
public final class ActionServerLuaCall extends AbilityActionHandler { public final class ActionServerLuaCall extends AbilityActionHandler {
@ -16,12 +17,14 @@ public final class ActionServerLuaCall extends AbilityActionHandler {
Ability ability, AbilityModifierAction action, ByteString abilityData, GameEntity target) { Ability ability, AbilityModifierAction action, ByteString abilityData, GameEntity target) {
var scene = target.getScene(); var scene = target.getScene();
var scriptManager = scene.getScriptManager(); var scriptManager = scene.getScriptManager();
var functionName = action.funcName; var functionName = action.funcName;
// Set the script library's manager. // Set the script library's manager.
var scriptLib = ScriptLoader.getScriptLib(); var scriptLib = ScriptLoader.getScriptLib();
scriptLib.setCurrentEntity(target); scriptLib.setCurrentEntity(target);
scriptLib.setSceneScriptManager(scriptManager); scriptLib.setSceneScriptManager(scriptManager);
// Attempt to call the function. // Attempt to call the function.
return switch (action.luaCallType) { return switch (action.luaCallType) {
default -> false; default -> false;
@ -33,7 +36,7 @@ public final class ActionServerLuaCall extends AbilityActionHandler {
// Set the script library's group. // Set the script library's group.
scriptLib.setCurrentGroup(group); scriptLib.setCurrentGroup(group);
yield ActionServerLuaCall.callFunction(script, functionName); yield ActionServerLuaCall.callFunction(script, functionName, ability, action);
} }
case SpecificGroup -> { case SpecificGroup -> {
var groupId = action.callParamList[0]; var groupId = action.callParamList[0];
@ -43,7 +46,16 @@ public final class ActionServerLuaCall extends AbilityActionHandler {
// Set the script library's group. // Set the script library's group.
scriptLib.setCurrentGroup(group); scriptLib.setCurrentGroup(group);
yield ActionServerLuaCall.callFunction(script, functionName); yield ActionServerLuaCall.callFunction(script, functionName, ability, action);
}
case Gadget -> {
var controller = target.getEntityController();
if (controller == null || functionName.isBlank()) yield false;
// Hand off the function handling to the controller.
controller.callControllerScriptFunc(target, functionName, ability, action);
yield true;
} }
}; };
} }
@ -53,17 +65,30 @@ public final class ActionServerLuaCall extends AbilityActionHandler {
* *
* @param bindings The bindings to fetch the function from. * @param bindings The bindings to fetch the function from.
* @param functionName The name of the function to call. * @param functionName The name of the function to call.
* @param ability The ability data.
* @param action The ability action data.
* @return Whether the function was called successfully. * @return Whether the function was called successfully.
*/ */
private static boolean callFunction(Bindings bindings, String functionName) { private static boolean callFunction(
Bindings bindings, String functionName, Ability ability, AbilityModifierAction action) {
try { try {
// Resolve the function from the script. // Resolve the function from the script.
var function = bindings.get(functionName); var function = bindings.get(functionName);
if (!(function instanceof LuaFunction luaFunction)) if (!(function instanceof LuaFunction luaFunction))
throw new Exception("Function is not a LuaFunction."); throw new Exception("Function is not a LuaFunction.");
// Attempt to invoke the function. // Convert parameters to Lua values.
luaFunction.call(ScriptLoader.getScriptLibLua()); var lParam1 = LuaValue.valueOf(action.param1.getInt(ability));
var lParam2 = LuaValue.valueOf(action.param2.getInt(ability));
var lParam3 = LuaValue.valueOf(action.param3.getInt(ability));
// Invoke the function with the parameters.
switch (action.paramNum) {
case 1 -> luaFunction.invoke(new LuaValue[] {lParam1});
case 2 -> luaFunction.invoke(new LuaValue[] {lParam1, lParam2});
case 3 -> luaFunction.invoke(new LuaValue[] {lParam1, lParam2, lParam3});
default -> luaFunction.invoke(new LuaValue[] {ScriptLoader.getScriptLibLua()});
}
return true; return true;
} catch (Exception exception) { } catch (Exception exception) {

View File

@ -24,6 +24,7 @@ public class PlayerActivityData {
int uid; int uid;
int activityId; int activityId;
Map<Integer, WatcherInfo> watcherInfoMap; Map<Integer, WatcherInfo> watcherInfoMap;
/** the detail data of each type of activity (Json format) */ /** the detail data of each type of activity (Json format) */
String detail; String detail;

View File

@ -86,6 +86,7 @@ public class Avatar {
// trial avatar property // trial avatar property
@Getter @Setter private int trialAvatarId = 0; @Getter @Setter private int trialAvatarId = 0;
// cannot store to db if grant reason is not integer // cannot store to db if grant reason is not integer
@Getter @Setter @Getter @Setter
private int grantReason = TrialAvatarGrantRecord.GrantReason.GRANT_REASON_INVALID.getNumber(); private int grantReason = TrialAvatarGrantRecord.GrantReason.GRANT_REASON_INVALID.getNumber();

View File

@ -49,6 +49,7 @@ public class GachaBanner {
@Getter private int[] rateUpItems5 = {}; @Getter private int[] rateUpItems5 = {};
// This now handles default values for the fields below // This now handles default values for the fields below
@Getter private BannerType bannerType = BannerType.STANDARD; @Getter private BannerType bannerType = BannerType.STANDARD;
// These don't change between banner types (apart from Standard having three extra 4star avatars) // These don't change between banner types (apart from Standard having three extra 4star avatars)
@Getter @Getter
private int[] fallbackItems3 = { private int[] fallbackItems3 = {

View File

@ -25,15 +25,13 @@ import it.unimi.dsi.fastutil.ints.*;
import java.nio.file.*; import java.nio.file.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import lombok.Getter; import lombok.Getter;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
public class GachaSystem extends BaseGameSystem { public class GachaSystem extends BaseGameSystem {
private static final int starglitterId = 221; private static final int starglitterId = 221;
private static final int stardustId = 222; private static final int stardustId = 222;
@Getter @Getter private final Int2ObjectMap<GachaBanner> gachaBanners;
private final Int2ObjectMap<GachaBanner> gachaBanners;
private WatchService watchService; private WatchService watchService;
public GachaSystem(GameServer server) { public GachaSystem(GameServer server) {
@ -422,8 +420,7 @@ public class GachaSystem extends BaseGameSystem {
if (this.watchService == null) { if (this.watchService == null) {
try { try {
this.watchService = FileSystems.getDefault().newWatchService(); this.watchService = FileSystems.getDefault().newWatchService();
FileUtils.getDataUserPath("") FileUtils.getDataUserPath("").register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger() Grasscutter.getLogger()
.error( .error(

View File

@ -195,6 +195,7 @@ public class GameMainQuest {
// .forEach(getQuestManager()::startMainQuest); // .forEach(getQuestManager()::startMainQuest);
// } // }
} }
// TODO // TODO
public void fail() {} public void fail() {}

View File

@ -7,6 +7,7 @@ import java.util.*;
public class TowerLevelRecord { public class TowerLevelRecord {
/** floorId in config */ /** floorId in config */
private int floorId; private int floorId;
/** LevelId - Stars */ /** LevelId - Stars */
private Map<Integer, Integer> passedLevelMap; private Map<Integer, Integer> passedLevelMap;

View File

@ -56,6 +56,7 @@ public class SceneScriptManager {
private final Map<Integer, SceneGroupInstance> cachedSceneGroupsInstances; private final Map<Integer, SceneGroupInstance> cachedSceneGroupsInstances;
private ScriptMonsterTideService scriptMonsterTideService; private ScriptMonsterTideService scriptMonsterTideService;
private ScriptMonsterSpawnService scriptMonsterSpawnService; private ScriptMonsterSpawnService scriptMonsterSpawnService;
/** blockid - loaded groupSet */ /** blockid - loaded groupSet */
private final Map<Integer, Set<SceneGroup>> loadedGroupSetPerBlock; private final Map<Integer, Set<SceneGroup>> loadedGroupSetPerBlock;
@ -846,6 +847,7 @@ public class SceneScriptManager {
.warn("failed to create entity with group {} and config {}", group.id, configId); .warn("failed to create entity with group {} and config {}", group.id, configId);
} }
} }
// Events // Events
public Future<?> callEvent(int groupId, int eventType) { public Future<?> callEvent(int groupId, int eventType) {
return callEvent(new ScriptArgs(groupId, eventType)); return callEvent(new ScriptArgs(groupId, eventType));

View File

@ -28,11 +28,13 @@ public class ScriptLoader {
@Getter private static Serializer serializer; @Getter private static Serializer serializer;
@Getter private static ScriptLib scriptLib; @Getter private static ScriptLib scriptLib;
@Getter private static LuaValue scriptLibLua; @Getter private static LuaValue scriptLibLua;
/** suggest GC to remove it if the memory is less */ /** suggest GC to remove it if the memory is less */
private static Map<String, SoftReference<String>> scriptSources = new ConcurrentHashMap<>(); private static Map<String, SoftReference<String>> scriptSources = new ConcurrentHashMap<>();
private static Map<String, SoftReference<CompiledScript>> scriptsCache = private static Map<String, SoftReference<CompiledScript>> scriptsCache =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
/** sceneId - SceneMeta */ /** sceneId - SceneMeta */
private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache = new ConcurrentHashMap<>(); private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache = new ConcurrentHashMap<>();

View File

@ -2,6 +2,7 @@ package emu.grasscutter.scripts.constants;
public class EventType { public class EventType {
public static final int EVENT_NONE = 0; public static final int EVENT_NONE = 0;
/** param1: monster.configId */ /** param1: monster.configId */
public static final int EVENT_ANY_MONSTER_DIE = 1; public static final int EVENT_ANY_MONSTER_DIE = 1;

View File

@ -18,6 +18,7 @@ public class SceneGadget extends SceneObject {
public int owner; public int owner;
public SceneBossChest boss_chest; public SceneBossChest boss_chest;
public int interact_id; public int interact_id;
/** /**
* Note: this field indicates whether the gadget should disappear permanently. For example, if * Note: this field indicates whether the gadget should disappear permanently. For example, if
* isOneOff=true, like most chests, it will disappear permanently after interacted. If * isOneOff=true, like most chests, it will disappear permanently after interacted. If

View File

@ -13,6 +13,7 @@ public abstract class SceneObject {
public Position pos; public Position pos;
public Position rot; public Position rot;
/** not set by lua */ /** not set by lua */
public transient SceneGroup group; public transient SceneGroup group;
} }

View File

@ -1,6 +1,8 @@
package emu.grasscutter.scripts.data.controller; package emu.grasscutter.scripts.data.controller;
import emu.grasscutter.*; import emu.grasscutter.*;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.game.ability.Ability;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.scripts.*; import emu.grasscutter.scripts.*;
@ -58,6 +60,30 @@ public class EntityController {
return 0; return 0;
} }
/**
* Invoked from {@link emu.grasscutter.game.ability.actions.ActionServerLuaCall} to call an entity
* controller function.
*
* @param entity The entity which called the function.
* @param funcName The name of the function to call.
* @param ability The ability that is calling the function.
* @param action The action that is calling the function.
* @return The return value of the function.
*/
public LuaValue callControllerScriptFunc(
GameEntity entity, String funcName, Ability ability, AbilityModifierAction action) {
var lParam1 = LuaValue.valueOf(action.param1.getInt(ability));
var lParam2 = LuaValue.valueOf(action.param2.getInt(ability));
var lParam3 = LuaValue.valueOf(action.param3.getInt(ability));
return switch (action.paramNum) {
case 1 -> this.callControllerScriptFunc(entity, funcName, lParam1);
case 2 -> this.callControllerScriptFunc(entity, funcName, lParam1, lParam2);
case 3 -> this.callControllerScriptFunc(entity, funcName, lParam1, lParam2, lParam3);
default -> this.callControllerScriptFunc(entity, funcName, LuaValue.NIL);
};
}
// TODO actual execution should probably be handle by EntityControllerScriptManager // TODO actual execution should probably be handle by EntityControllerScriptManager
private LuaValue callControllerScriptFunc(GameEntity entity, String funcName, LuaValue arg1) { private LuaValue callControllerScriptFunc(GameEntity entity, String funcName, LuaValue arg1) {
return callControllerScriptFunc(entity, funcName, arg1, LuaValue.NIL, LuaValue.NIL); return callControllerScriptFunc(entity, funcName, arg1, LuaValue.NIL, LuaValue.NIL);

View File

@ -22,8 +22,8 @@ import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger; import org.slf4j.Logger;
/** /**
* This is a simple implementation of a server-to-server IPC client. * This is a simple implementation of a server-to-server IPC client. It is implemented over
* It is implemented over WebSockets, and supports all Grasscutter versions past 1.6.0 * WebSockets, and supports all Grasscutter versions past 1.6.0
*/ */
@Getter @Getter
public final class DispatchClient extends WebSocketClient implements IDispatcher { public final class DispatchClient extends WebSocketClient implements IDispatcher {

View File

@ -14,6 +14,7 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
/** Handles requests for the new GM Handbook. */ /** Handles requests for the new GM Handbook. */
@Deprecated(since = "1.7.5-dev")
public final class HandbookHandler implements Router { public final class HandbookHandler implements Router {
private String handbook; private String handbook;
private final boolean serve; private final boolean serve;

View File

@ -16,9 +16,10 @@ public class HandlerAvatarUpgradeReq extends PacketHandler {
// These are bundled into a list of items. // These are bundled into a list of items.
for (var item : req.getItemParamListList()) { for (var item : req.getItemParamListList()) {
session session
.getServer() .getServer()
.getInventorySystem() .getInventorySystem()
.upgradeAvatar(session.getPlayer(), req.getAvatarGuid(), item.getItemId(), item.getCount()); .upgradeAvatar(
session.getPlayer(), req.getAvatarGuid(), item.getItemId(), item.getCount());
} }
} }
} }

View File

@ -28,8 +28,10 @@ public class PacketPlayerEnterSceneNotify extends BasePacket {
.setTargetUid(player.getUid()) .setTargetUid(player.getUid())
.setEnterSceneToken(player.getEnterSceneToken()) .setEnterSceneToken(player.getEnterSceneToken())
.setWorldLevel(player.getWorldLevel()) .setWorldLevel(player.getWorldLevel())
// .setEnterReason(EnterReason.Login.getValue()) // Removed in 5.0; we don't know what it is // .setEnterReason(EnterReason.Login.getValue()) // Removed in 5.0; we don't know what
// .setIsFirstLoginEnterScene(player.isFirstLoginEnterScene()) // Removed in 5.0; we don't know what it is // it is
// .setIsFirstLoginEnterScene(player.isFirstLoginEnterScene()) // Removed in 5.0; we
// don't know what it is
// .setWorldType(1) // Removed in 5.0; we don't know what it is // .setWorldType(1) // Removed in 5.0; we don't know what it is
.setSceneTransaction( .setSceneTransaction(
"3-" "3-"
@ -80,7 +82,8 @@ public class PacketPlayerEnterSceneNotify extends BasePacket {
var proto = var proto =
PlayerEnterSceneNotify.newBuilder() PlayerEnterSceneNotify.newBuilder()
// .setPrevSceneId(player.getSceneId()) // Removed in 5.0; we don't know what it is // .setPrevSceneId(player.getSceneId()) // Removed in 5.0; we don't know what it is
// .setPrevPos(player.getPosition().toProto()) // Removed in 5.0; we don't know what it is // .setPrevPos(player.getPosition().toProto()) // Removed in 5.0; we don't know what it
// is
.setSceneId(teleportProperties.getSceneId()) .setSceneId(teleportProperties.getSceneId())
.setPos(teleportProperties.getTeleportTo().toProto()) .setPos(teleportProperties.getTeleportTo().toProto())
.setSceneBeginTime(System.currentTimeMillis()) .setSceneBeginTime(System.currentTimeMillis())
@ -88,7 +91,8 @@ public class PacketPlayerEnterSceneNotify extends BasePacket {
.setTargetUid(target.getUid()) .setTargetUid(target.getUid())
.setEnterSceneToken(player.getEnterSceneToken()) .setEnterSceneToken(player.getEnterSceneToken())
.setWorldLevel(target.getWorld().getWorldLevel()) .setWorldLevel(target.getWorld().getWorldLevel())
// .setEnterReason(teleportProperties.getEnterReason().getValue()) // Removed in 5.0; we don't know what it is // .setEnterReason(teleportProperties.getEnterReason().getValue()) // Removed in 5.0; we
// don't know what it is
// .setWorldType(1) // Removed in 5.0; we don't know what it is // .setWorldType(1) // Removed in 5.0; we don't know what it is
.setSceneTransaction( .setSceneTransaction(
teleportProperties.getSceneId() teleportProperties.getSceneId()
@ -118,14 +122,16 @@ public class PacketPlayerEnterSceneNotify extends BasePacket {
var proto = var proto =
PlayerEnterSceneNotify.newBuilder() PlayerEnterSceneNotify.newBuilder()
// .setPrevSceneId(player.getSceneId()) // Removed in 5.0; we don't know what it is // .setPrevSceneId(player.getSceneId()) // Removed in 5.0; we don't know what it is
// .setPrevPos(player.getPosition().toProto()) // Removed in 5.0; we don't know what it is // .setPrevPos(player.getPosition().toProto()) // Removed in 5.0; we don't know what it
// is
.setSceneId(teleportProperties.getSceneId()) .setSceneId(teleportProperties.getSceneId())
.setPos(teleportProperties.getTeleportTo().toProto()) .setPos(teleportProperties.getTeleportTo().toProto())
.setSceneBeginTime(System.currentTimeMillis()) .setSceneBeginTime(System.currentTimeMillis())
.setType(other ? EnterType.ENTER_TYPE_OTHER_HOME : EnterType.ENTER_TYPE_SELF_HOME) .setType(other ? EnterType.ENTER_TYPE_OTHER_HOME : EnterType.ENTER_TYPE_SELF_HOME)
.setTargetUid(targetUid) .setTargetUid(targetUid)
.setEnterSceneToken(player.getEnterSceneToken()) .setEnterSceneToken(player.getEnterSceneToken())
// .setEnterReason(teleportProperties.getEnterReason().getValue()) // Removed in 5.0; we don't know what it is // .setEnterReason(teleportProperties.getEnterReason().getValue()) // Removed in 5.0; we
// don't know what it is
// .setWorldType(64) // Removed in 5.0; we don't know what it is // .setWorldType(64) // Removed in 5.0; we don't know what it is
.setSceneTransaction( .setSceneTransaction(
teleportProperties.getSceneId() teleportProperties.getSceneId()

View File

@ -58,7 +58,8 @@ public class PacketPlayerLoginRsp extends BasePacket {
.setClientDataVersion(info.getClientDataVersion()) .setClientDataVersion(info.getClientDataVersion())
.setClientSilenceDataVersion(info.getClientSilenceDataVersion()) .setClientSilenceDataVersion(info.getClientSilenceDataVersion())
// .setClientMd5(info.getClientDataMd5()) // Removed in 5.0; we don't know what it is // .setClientMd5(info.getClientDataMd5()) // Removed in 5.0; we don't know what it is
// .setClientSilenceMd5(info.getClientSilenceDataMd5()) // Removed in 5.0; we don't know what it is // .setClientSilenceMd5(info.getClientSilenceDataMd5()) // Removed in 5.0; we don't know
// what it is
.setResVersionConfig(info.getResVersionConfig()) .setResVersionConfig(info.getResVersionConfig())
.setClientVersionSuffix(info.getClientVersionSuffix()) .setClientVersionSuffix(info.getClientVersionSuffix())
.setClientSilenceVersionSuffix(info.getClientSilenceVersionSuffix()) .setClientSilenceVersionSuffix(info.getClientSilenceVersionSuffix())

View File

@ -199,10 +199,10 @@ public final class FileUtils {
} }
public static byte[] readResource(String resourcePath) { public static byte[] readResource(String resourcePath) {
try (InputStream is = Grasscutter.class.getResourceAsStream(resourcePath)) { try (var is = Grasscutter.class.getResourceAsStream(resourcePath)) {
return is.readAllBytes(); return Objects.requireNonNull(is).readAllBytes();
} catch (Exception exception) { } catch (Exception exception) {
Grasscutter.getLogger().warn("Failed to read resource: " + resourcePath); Grasscutter.getLogger().warn("Failed to read resource: {}", resourcePath);
Grasscutter.getLogger().debug("Failed to load resource: " + resourcePath, exception); Grasscutter.getLogger().debug("Failed to load resource: " + resourcePath, exception);
} }

View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
margin: 0;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
p {
margin: 2px;
}
</style>
<title>Handbook Authentication</title>
</head>
<body>
<script type="application/javascript">
if ("{{VALUE}}" === "true") {
parent.postMessage({
type: "handbook-auth",
token: "{{SESSION_TOKEN}}",
uid: "{{PLAYER_ID}}"
}, "*");
}
</script>
<p>Input your Player UID here.</p>
<form method="post">
<label>
<input
name="playerid"
type="number"
/>
</label>
<input type="submit" />
</form>
</body>
</html>