Compare commits

..

21 Commits

  • Fix a typo I left in Player.java (#2180)
    ENTER_REGION_ ->LEAVE_REGION_
    Took the opportunity to refactor it so that we only calculate the string once.
  • README.md (#2181)
    Remove mention of unstable branch in README.md
  • Fix issues with regions (#2179)
    Luckily, SceneRegion and TriggerExcelConfigData both have group numbers, so we can use those to differentiate regions!
  • Merge unstable into development (#2173)
    * Remove more scene synchronized
    
    * Fix worktop options not appearing
    
    * Format code [skip actions]
    
    * Fix delay with server tasks
    
    * Format code [skip actions]
    
    * Fully fix fairy clock (#2146)
    
    * Fix scene transition
    
    * fully fix fairy clock
    
    * Re-add call to `Player#updatePlayerGameTime`
    
    * Format code [skip actions]
    
    * Initialize the script loader in `ResourceLoader#loadAll`
    
    * Fix region removal checking
    
    * Format code [skip actions]
    
    * Use Lombok's `EqualsAndHashCode` for comparing scene regions
    
    * Format code [skip actions]
    
    * Move 'invalid gather object' to `trace`
    
    * Add more information to the 'unknown condition handler' message
    
    * Move invalid ability action to trace
    
    * Make `KcpTunnel` public
    
    * Validate the NPC being talked to
    
    * Format code [skip actions]
    
    * NPCs are not spawned server side; change logic to handle it
    
    * Format code [skip actions]
    
    * unload scene when there are no players (#2147)
    
    * unload scene when there are no players
    
    * Update src/main/java/emu/grasscutter/game/world/Scene.java
    
    Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>
    
    * Check if a command should be copied or HTTP should be used
    
    * Lint Code [skip actions]
    
    * Fix character names rendering incorrectly
    
    * Add basic troubleshooting command
    
    * Implement handbook teleporting
    
    also a few formatting changes and sort data by logical sense
    
    * Fix listener `ConcurrentModificationException` issue
    
    * Add color change to `Join the Community!`
    
    * Lint Code [skip actions]
    
    * Make clickable buttons appear clickable
    
    * Remove 'Mechanicus' entities from the list of entities
    
    * Format code [skip actions]
    
    * Fix going back returning a blank screen
    
    * Implement entity spawning
    
    * Add setting level to entity card
    
    * Add support for 'plain text' mode
    
    * Make descriptions of objects scrollable
    
    * Lint Code [skip actions]
    
    * Format code [skip actions]
    
    * Change the way existing hooks work
    
    * Format code [skip actions]
    
    * Upgrade Javalin to 5.5.0 & Fix project warnings
    
    * Upgrade logging libraries
    
    * Fix gacha mappings static file issue
    
    * Add temporary backwards compatability for `ServerHelper`
    
    * Format code [skip actions]
    
    * Remove artifact signatures from VCS
    
    * Fix forge queue data protocol definition
    
    * Run `spotlessApply`
    
    * Format code [skip actions]
    
    * Download data required for building artifacts
    
    * Add call for Facebook logins
    
    * Add the wiki page as a submodule
    
    * Format code [skip actions]
    
    * Update translation (#2150)
    
    * Update translation
    
    * Update translation
    
    * Separate the dispatch and game servers (pt. 1)
    
    gacha is still broken, handbook still needs to be done
    
    * Format code [skip actions]
    
    * Separate the dispatch and game servers (pt. 2)
    
    this commit fixes the gacha page
    
    * Add description for '/troubleshoot'
    
    * Set default avatar talent level to 10
    
    * Separate the dispatch and game servers (pt. 3)
    
    implement handbook across servers!
    
    * Format code [skip actions]
    
    * Update GitHub Actions to use 'download-file' over 'wget'
    
    * Gm handbook lmao (#2149)
    
    * Fix font issue
    
    * Fix avatars
    
    * Fix text overflow in commands
    
    * Fix virtualized lists and items page 😭😭
    
    * magix why 💀
    
    * use hover style in all minicards
    
    * button
    
    * remove console.log
    
    * lint
    
    * Add icons
    
    * magix asked
    
    * Fix overflow padding issue
    
    * Fix achievement text overflow
    
    * remove icons from repo
    
    * Change command icon
    
    * Add the wiki page as a submodule
    
    * total magix moment
    
    * fix text overflow in commands
    
    * Fix discord button
    
    * Make text scale on Minicard
    
    * import icons and font from another source
    
    * Add hover effects to siebar buttons
    
    * move font and readme to submodule repo
    
    * Make data folder a submodule
    
    * import icons and font from data submodule
    
    * Update README.md
    
    * total magix moment
    
    * magix moment v2
    
    * submodule change
    
    * Import `.webp` files
    
    * Resize `HomeButton`
    
    * Fix 'Copy Command' reappearing after changing pages
    
    ---------
    
    Co-authored-by: KingRainbow44 <kobedo11@gmail.com>
    
    * Lint Code [skip actions]
    
    * Download data for the build, not for the lint
    
    * format imports
    
    this is really just to see if build handbook works kek
    
    * Implement proper handbook authentication (pt. 1)
    
    * Implement proper handbook authentication (pt. 2)
    
    * Format code [skip actions]
    
    * Add quest data dumping for the handbook
    
    * Change colors to fit _something suitable_
    
    * Format code [skip actions]
    
    * Fix force pushing to branches after linting
    
    * Fix logic of `SetPlayerPropReq`
    
    * Move more group loading to `trace`
    
    * Add handbook IP authentication in hybrid mode
    
    * Fix player level up not displaying on the client properly
    
    * Format code [skip actions]
    
    * Fix game time locking
    
    * Format code [skip actions]
    
    * Update player properties
    
    * Format code [skip actions]
    
    * Move `warn`s for groups to `debug`
    
    * Fix player pausing
    
    * Move more logs to `trace`
    
    * Use `removeItemById` for deleting items via quests
    
    * Clean up logger more
    
    * Pause in-game time when the world is paused
    
    * Format code [skip actions]
    
    * More player property documentation
    
    * Multi-threaded resource loading
    
    * Format code [skip actions]
    
    * Add quest widgets
    
    * Add quests page (basic impl.)
    
    * Add/fix colors
    
    also fix tailwind
    
    * Remove banned packets
    
    client modifications already perform the job of blocking malicious packets from being executed, no point in having this if self-windy is wanted
    
    * Re-add `BeginCameraSceneLookNotify`
    
    * Fix being unable to attack (#2157)
    
    * Add `PlayerOpenChestEvent`
    
    * Add methods to get players from the server
    
    * Add static methods to register an event handler
    
    * Add `PlayerEnterDungeonEvent`
    
    * Remove legacy documentation from `PlayerMoveEvent`
    
    * Add `PlayerChatEvent`
    
    * Add defaults to `Position`
    
    * Clean up `.utils`
    
    * Revert `Multi-threaded resource loading`
    
    * Fix changing target UID when talking to the server
    
    * Lint Code [skip actions]
    
    * Format code [skip actions]
    
    * fix NPC talk triggering main quest in 46101 (#2158)
    
    Make it so that only talks where the param matches the talkId are checked.
    
    * Format code [skip actions]
    
    * Partially fix Chasing Shadows (#2159)
    
    * Partially fix Chasing Shadows
    
    * Go ahead and move it before the return before Magix tells me to.
    
    * Format code [skip actions]
    
    * Bring back period lol (#2160)
    
    * Disable SNI for the HTTPS server
    
    * Add `EntityCreationEvent`
    
    * Add initial startup message
    
    this is so the server appears like its preparing to start
    
    * Format code [skip actions]
    
    * Enable debug mode for plugin loggers if enabled for the primary logger
    
    * Add documentation about `WorldAreaConfigData`
    
    * Make more fields in excels accessible
    
    * Remove deprecated fields from `GetShopRsp`
    
    * Run `spotlessApply` on definitions
    
    * Add `PlayerEnterAreaEvent`
    
    * Optimize event calls
    
    * Fix event invokes
    
    * Format code [skip actions]
    
    * Remove manual autofinish for main quests. (#2162)
    
    * Add world areas to the textmap cache
    
    * Format code [skip actions]
    
    * Don't overdefine variables in extended classes (#2163)
    
    * Add dumper for world areas
    
    * Format code [skip actions]
    
    * instantiate personalLineList (#2165)
    
    * Fix protocol definitions
    
    thank you Nazrin! (+ hiro for raw definitions)
    
    * Fix the background color leaking from the character widget
    
    * Change HTML spacing to 2 spaces
    
    * Implement hiding widgets
    
    * Change scrollbar to a vibrant color
    
    * Add _some_ scaling to the home buttons and its text
    
    * Build the handbook with Gradle
    
    * Fix the 'finer details' with the handbook UI
    
    * Lint Code [skip actions]
    
    * Fix target destination for the Gradle-built handbook
    
    * Implement fetching a player across servers & Add a chainable JsonObject
    
    useful for plugins! might be used in grasscutter eventually
    
    * Fix GitHub actions
    
    * Fix event calling & canceling
    
    * Run `spotlessApply`
    
    * Rename fields (might be wrong)
    
    * Add/update all/more protocol definitions
    
    * Add/update all/more protocol definitions
    
    * Remove outdated packet
    
    * Fix protocol definitions
    
    * Format code [skip actions]
    
    * Implement some lua variables for less console spam (#2172)
    
    * Implement some lua variables for less console spam
    
    * Add GetHostQuestState
    
    This fixes some chapter 3 stuff.
    
    * Format code [skip actions]
    
    * Fix merge import
    
    * Format code [skip actions]
    
    * Fully fix fairy clock for real this time (#2167)
    
    * Fully fix fairy clock For real this time
    
    * Make it so relogging keeps the time lock state.
    
    * Refactor out questLockTime
    
    * Per Hartie, the client packet needs to be changed too
    
    * Update src/main/java/emu/grasscutter/game/world/World.java
    
    Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>
    
    * Update src/main/java/emu/grasscutter/server/packet/recv/HandlerClientLockGameTimeNotify.java
    
    * Remove all code not needed to get clock working
    
    ---------
    
    Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>
    
    * Implement a proper ability system (#2166)
    
    * Apply fix `21dec2fe`
    
    * Apply fix `89d01d5f`
    
    * Apply fix `d900f154`
    
    this one was already implemented; updated to use call from previous commit
    
    * Ability changing commit
    
    TODO: change info to debug
    
    * Remove use of deprecated methods/fields
    
    * Temp commit v2
    (Adding LoseHP and some fixes)
    
    * Oopsie
    
    * Probably fix monster battle
    
    * Fix issue with reflecting into fields
    
    * Fix some things
    
    * Fix ability names for 3.6 resources
    
    * Improve logging
    
    ---------
    
    Co-authored-by: StartForKiller <jesussanz2003@gmail.com>
    
    * Format code [skip actions]
    
    * Add system for sending messages between servers
    
    * Format some code
    
    * Remove protocol definitions from Spotless
    
    * Default debug to false; enable with `-debug`
    
    * Implement completely useless global value copying
    
    * HACK: Return the avatar which holds the weapon when the weapon is referred to by ID
    
    * Add properties to `AbilityModifier`
    
    * Change the way HTML is served after authentication
    
    * Use thread executors to speed up the database loading process
    
    * Format code [skip actions]
    
    * Add system for setting handbook address and port
    
    * Lint Code [skip actions]
    
    * Format code [skip actions]
    
    * Fix game-related data not saving
    
    * Format code [skip actions]
    
    * Fix handbook server details
    
    * Lint Code [skip actions]
    
    * Format code [skip actions]
    
    * Use the headers provided by a context to get the IP address
    
    should acknowledge #1975
    
    * Format code [skip actions]
    
    * Move more logs to `trace`
    
    * Format code [skip actions]
    
    * more trace
    
    * Fix something and implement weapon entities
    
    * Format code [skip actions]
    
    * Fix `EntityWeapon`
    
    * Remove deprecated API & Fix resource checking
    
    * Fix unnecessary warning for first-time setup
    
    * Implement handbook request limiting
    
    * Format code [skip actions]
    
    * Fix new avatar weapons being null
    
    * Format code [skip actions]
    
    * Fix issue with 35303 being un-completable & Try to fix fulfilled quest conditions being met
    
    * Load activity config on server startup
    
    * Require plugins to specify an API version and match with the server
    
    * Add default open state ignore list
    
    * Format code [skip actions]
    
    * Quick fix for questing, needs more investigation
    This would make the questing work again
    
    * Remove existing hack for 35303
    
    * Fix ignored open states from being set
    
    * Format code [skip actions]
    
    * fix the stupidest bug ive ever seen
    
    * Optimize player kicking on server close
    
    * Format code [skip actions]
    
    * Re-add hack to fix 35303
    
    * Update GitHub actions
    
    * Format code [skip actions]
    
    * Potentially fix issues with regions
    
    * Download additional handbook data
    
    * Revert "Potentially fix issues with regions"
    
    This reverts commit 84e3823695.
    
    ---------
    
    Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
    Co-authored-by: scooterboo <lewasite@yahoo.com>
    Co-authored-by: Tesutarin <105267106+Tesutarin@users.noreply.github.com>
    Co-authored-by: Scald <104459145+Arikatsu@users.noreply.github.com>
    Co-authored-by: StartForKiller <jesussanz2003@gmail.com>
41 changed files with 336 additions and 207 deletions
+1 -2
View File
@@ -4,7 +4,6 @@ on:
pull_request_target:
types:
- opened
- reopened
branches:
- development
paths:
@@ -20,4 +19,4 @@ jobs:
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: "This PR has been closed for modifying protected files. See `CONTRIBUTING.md` for more information."
comment: "This PR has been closed for modifying protected files. See `CONTRIBUTING.md` for more information."
+2 -2
View File
@@ -80,7 +80,7 @@ Grasscutter uses Gradle to handle dependencies & building.
##### Windows
```shell
git clone --recurse-submodules -b unstable https://github.com/Grasscutters/Grasscutter.git
git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git
cd Grasscutter
.\gradlew.bat # Setting up environments
.\gradlew jar # Compile
@@ -89,7 +89,7 @@ cd Grasscutter
##### Linux (GNU)
```bash
git clone --recurse-submodules -b unstable https://github.com/Grasscutters/Grasscutter.git
git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git
cd Grasscutter
chmod +x gradlew
./gradlew jar # Compile
+1 -1
View File
@@ -57,7 +57,7 @@ sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
group = 'io.grasscutter'
version = '2.0.0-unstable'
version = '1.6.1'
java {
withJavadocJar()
+25 -8
View File
@@ -1,14 +1,11 @@
package emu.grasscutter;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
import ch.qos.logback.classic.*;
import emu.grasscutter.auth.*;
import emu.grasscutter.command.*;
import emu.grasscutter.config.ConfigContainer;
import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.database.DatabaseManager;
import emu.grasscutter.database.*;
import emu.grasscutter.plugin.PluginManager;
import emu.grasscutter.plugin.api.ServerHelper;
import emu.grasscutter.server.dispatch.DispatchServer;
@@ -21,16 +18,20 @@ import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.*;
import emu.grasscutter.utils.lang.Language;
import io.netty.util.concurrent.FastThreadLocalThread;
import java.io.*;
import java.util.Calendar;
import java.util.concurrent.*;
import javax.annotation.Nullable;
import lombok.*;
import org.jline.reader.*;
import org.jline.terminal.*;
import org.reflections.Reflections;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.*;
import java.util.Calendar;
import java.util.concurrent.*;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
public final class Grasscutter {
public static final File configFile = new File("./config.json");
public static final Reflections reflector = new Reflections("emu.grasscutter");
@@ -183,6 +184,22 @@ public final class Grasscutter {
private static void onShutdown() {
// Disable all plugins.
if (pluginManager != null) pluginManager.disablePlugins();
try {
// Wait for Grasscutter's thread pool to finish.
var executor = Grasscutter.getThreadPool();
executor.shutdown();
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
// Wait for database operations to finish.
var dbExecutor = DatabaseHelper.getEventExecutor();
dbExecutor.shutdown();
if (!dbExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
dbExecutor.shutdownNow();
}
} catch (InterruptedException ignored) { }
}
/*
@@ -1,12 +1,13 @@
package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.lang.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.command.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameQuest;
import java.util.List;
import java.util.stream.Collectors;
import static emu.grasscutter.utils.lang.Language.translate;
@Command(
label = "quest",
@@ -131,6 +132,22 @@ public final class QuestCommand implements CommandHandler {
"Triggers registered for %s: %s."
.formatted(questId, String.join(", ", quest.getTriggers().keySet())));
}
case "grouptriggers" -> {
var scene = targetPlayer.getScene();
var scriptManager = scene.getScriptManager();
var group = scriptManager.getGroupById(questId);
if (group == null) {
CommandHandler.sendMessage(sender, "The group does not exist.");
return;
}
CommandHandler.sendMessage(sender,
group.triggers.entrySet().stream()
.map(entry -> "%s: %s".formatted(entry.getKey(), entry.getValue()))
.collect(Collectors.joining(", "))
);
}
default -> this.sendUsageMessage(sender);
}
}
@@ -17,19 +17,21 @@ import static emu.grasscutter.Grasscutter.*;
public class ConfigContainer {
/*
* Configuration changes:
* Version 5 - 'questing' has been changed from a boolean
* to a container of options ('questOptions').
* This field will be removed in future versions.
* Version 6 - 'questing' has been fully replaced with 'questOptions'.
* The field for 'legacyResources' has been removed.
* Version 7 - 'regionKey' is being added for authentication
* with the new dispatch server.
* Version 8 - 'server' is being added for enforcing handbook server
* addresses.
* Version 9 - 'limits' was added for handbook requests.
* Version 5 - 'questing' has been changed from a boolean
* to a container of options ('questOptions').
* This field will be removed in future versions.
* Version 6 - 'questing' has been fully replaced with 'questOptions'.
* The field for 'legacyResources' has been removed.
* Version 7 - 'regionKey' is being added for authentication
* with the new dispatch server.
* Version 8 - 'server' is being added for enforcing handbook server
* addresses.
* Version 9 - 'limits' was added for handbook requests.
* Version 10 - 'trialCostumes' was added for enabling costumes
* on trial avatars.
*/
private static int version() {
return 9;
return 10;
}
/**
@@ -255,6 +257,8 @@ public class ConfigContainer {
public boolean staminaUsage = true;
public boolean energyUsage = true;
public boolean fishhookTeleport = true;
public boolean trialCostumes = false;
@SerializedName(value = "questing", alternate = "questOptions")
public Questing questing = new Questing();
public ResinOptions resinOptions = new ResinOptions();
@@ -87,7 +87,10 @@ public class AbilityData {
}
private void initializeModifiers() {
if (modifiers == null) return;
if (modifiers == null) {
this.modifiers = new HashMap<>();
return;
}
var _modifiers =
modifiers.entrySet().stream()
@@ -1,12 +1,11 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.*;
import emu.grasscutter.game.talk.TalkExec;
import lombok.*;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ResourceType(name = "TalkExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@@ -38,6 +37,14 @@ public final class TalkConfigData extends GameResource {
this.finishExec == null
? List.of()
: this.finishExec.stream().filter(x -> x.getType() != null).toList();
if (this.questId <= 0) {
var id = String.valueOf(this.getId());
this.questId = Integer.parseInt(
id.length() < 5 ? "0" :
id.substring(0, 3)
);
}
}
@Data
@@ -1,12 +1,8 @@
package emu.grasscutter.database;
import static com.mongodb.client.model.Filters.eq;
import dev.morphia.query.FindOptions;
import dev.morphia.query.Sort;
import dev.morphia.query.*;
import dev.morphia.query.experimental.filters.Filters;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.*;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.achievement.Achievements;
import emu.grasscutter.game.activity.PlayerActivityData;
@@ -23,12 +19,16 @@ import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.utils.objects.Returnable;
import io.netty.util.concurrent.FastThreadLocalThread;
import lombok.Getter;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Stream;
import static com.mongodb.client.model.Filters.eq;
public final class DatabaseHelper {
private static final ExecutorService eventExecutor =
@Getter private static final ExecutorService eventExecutor =
new ThreadPoolExecutor(
6,
6,
@@ -1,7 +1,5 @@
package emu.grasscutter.game.avatar;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import dev.morphia.annotations.*;
import emu.grasscutter.GameConstants;
import emu.grasscutter.data.GameData;
@@ -32,12 +30,15 @@ import emu.grasscutter.net.proto.TrialAvatarInfoOuterClass.TrialAvatarInfo;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.helpers.ProtoHelper;
import it.unimi.dsi.fastutil.ints.*;
import java.util.*;
import java.util.stream.Stream;
import javax.annotation.*;
import lombok.*;
import org.bson.types.ObjectId;
import javax.annotation.*;
import java.util.*;
import java.util.stream.Stream;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
@Entity(value = "avatars", useDiscriminator = false)
public class Avatar {
@Transient @Getter private final Int2ObjectMap<GameItem> equips;
@@ -1243,13 +1244,15 @@ public class Avatar {
});
// Add costume if avatar has a costume.
GameData.getAvatarCostumeDataItemIdMap()
if (GAME_OPTIONS.trialCostumes) {
GameData.getAvatarCostumeDataItemIdMap()
.values()
.forEach(
costumeData -> {
if (costumeData.getCharacterId() != this.getAvatarId()) return;
this.setCostume(costumeData.getId());
});
costumeData -> {
if (costumeData.getCharacterId() != this.getAvatarId()) return;
this.setCostume(costumeData.getId());
});
}
}
/** Equips the items applied from {@link Avatar#applyTrialItems()}. */
@@ -75,6 +75,7 @@ public class GadgetChest extends GadgetContent {
} else if (chest.chest_drop_id != 0) {
status = dropSystem.handleChestDrop(chest.chest_drop_id, chest.drop_count, getGadget());
}
if (status) {
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(
@@ -317,7 +317,9 @@ public class StaminaManager extends BasePlayerManager {
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
player.getScene().removeEntity(entity);
((EntityAvatar) entity).onDeath(dieType, 0);
if (entity instanceof EntityAvatar avatar)
avatar.onDeath(dieType, 0);
}
public void startSustainedStaminaHandler() {
@@ -345,8 +345,9 @@ public class Player implements PlayerHook, FieldFetch {
this.playerGameTime = gameTime;
// If the player is the host of the world, update the game time as well.
if (this.getWorld().getHost() == this) {
this.getWorld().changeTime(gameTime);
var world = this.getWorld();
if (world != null && world.getHost() == this) {
world.changeTime(gameTime);
}
// Trigger the script event for game time update.
@@ -707,14 +708,16 @@ public class Player implements PlayerHook, FieldFetch {
}
public void onEnterRegion(SceneRegion region) {
var enterRegionName = "ENTER_REGION_" + region.config_id;
this.getQuestManager().forEachActiveQuest(quest -> {
if (quest.getTriggerData() != null &&
quest.getTriggers().containsKey("ENTER_REGION_"+ region.config_id)) {
quest.getTriggers().containsKey(enterRegionName) &&
region.getGroupId() == quest.getTriggerData().get(enterRegionName).getGroupId()) {
// If trigger hasn't been fired yet
if (!Boolean.TRUE.equals(quest.getTriggers().put("ENTER_REGION_" + region.config_id, true))) {
if (!Boolean.TRUE.equals(quest.getTriggers().put(enterRegionName, true))) {
this.getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
this.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_TRIGGER_FIRE,
quest.getTriggerData().get("ENTER_REGION_" + region.config_id).getId(), 0);
quest.getTriggerData().get(enterRegionName).getId(), 0);
}
}
});
@@ -722,13 +725,15 @@ public class Player implements PlayerHook, FieldFetch {
}
public void onLeaveRegion(SceneRegion region) {
var leaveRegionName = "LEAVE_REGION_" + region.config_id;
this.getQuestManager().forEachActiveQuest(quest -> {
if (quest.getTriggers().containsKey("LEAVE_REGION_" + region.config_id)) {
if (quest.getTriggers().containsKey(leaveRegionName) &&
region.getGroupId() == quest.getTriggerData().get(leaveRegionName).getGroupId()) {
// If trigger hasn't been fired yet
if (!Boolean.TRUE.equals(quest.getTriggers().put("LEAVE_REGION_" + region.config_id, true))) {
if (!Boolean.TRUE.equals(quest.getTriggers().put(leaveRegionName, true))) {
this.getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
this.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_TRIGGER_FIRE,
quest.getTriggerData().get("LEAVE_REGION_" + region.config_id).getId(), 0);
quest.getTriggerData().get(leaveRegionName).getId(), 0);
}
}
});
@@ -1,12 +1,12 @@
package emu.grasscutter.game.player;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import dev.morphia.annotations.Entity;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.net.proto.AvatarTeamOuterClass.AvatarTeam;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
@Entity
public final class TeamInfo {
@@ -87,6 +87,8 @@ public final class TeamInfo {
for (int i = 0; i < this.getAvatars().size(); i++) {
Avatar avatar = player.getAvatars().getAvatarById(this.getAvatars().get(i));
if (avatar == null) continue;
avatarTeam.addAvatarGuidList(avatar.getGuid());
}
@@ -16,11 +16,12 @@ import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.ConversionUtils;
import java.util.*;
import java.util.stream.Collectors;
import lombok.*;
import org.bson.types.ObjectId;
import java.util.*;
import java.util.stream.Collectors;
@Entity(value = "quests", useDiscriminator = false)
public class GameMainQuest {
@Id private ObjectId id;
@@ -314,7 +315,7 @@ public class GameMainQuest {
0, new Position(avatarPosPos.get(0), avatarPosPos.get(1), avatarPosPos.get(2))); // position
posAndRot.add(
1, new Position(avatarPosRot.get(0), avatarPosRot.get(1), avatarPosRot.get(2))); // rotation
Grasscutter.getLogger().info("Succesfully loaded rewind data for subQuest {}", subId);
Grasscutter.getLogger().debug("Successfully loaded rewind data for quest {}.", subId);
return true;
}
@@ -15,10 +15,11 @@ import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.IntIntImmutablePair;
import java.util.*;
import javax.script.Bindings;
import lombok.*;
import javax.script.Bindings;
import java.util.*;
@Entity
public class GameQuest {
@Transient @Getter @Setter private GameMainQuest mainQuest;
@@ -142,7 +143,7 @@ public class GameQuest {
}
public void setFinishProgress(int index, int value) {
finishProgressList[index] = value;
this.finishProgressList[index] = value;
}
public void setFailProgress(int index, int value) {
@@ -111,7 +111,9 @@ public class QuestManager extends BasePlayerManager {
30700, // Quest which is responsible for unlocking Crash Course.
30800, // Quest which is responsible for unlocking Sparks Amongst the Pages.
47001, 47002, 47003, 47004
47001, 47002, 47003, 47004,
2010103, 2010144 // Prologue Act 2: Chasing Shadows
));
}
}
@@ -1,12 +1,11 @@
package emu.grasscutter.game.quest.content;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_COMPLETE_TALK;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.*;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_COMPLETE_TALK;
@QuestValueContent(QUEST_CONTENT_COMPLETE_TALK)
public class ContentCompleteTalk extends BaseContent {
@Override
@@ -53,7 +53,8 @@ public class ExecNotifyGroupLua extends QuestExecHandler {
? EventType.EVENT_QUEST_FINISH
: EventType.EVENT_QUEST_START;
scriptManager.callEvent(
new ScriptArgs(groupId, eventType, quest.getSubQuestId())
new ScriptArgs(groupId, eventType, quest.getSubQuestId(),
quest.getState() == QuestState.QUEST_STATE_FINISHED ? 1 : 0)
.setEventSource(quest.getSubQuestId()));
});
@@ -1,15 +1,13 @@
package emu.grasscutter.game.talk;
import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_COMPLETE_TALK;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_COMPLETE_ANY_TALK;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_COMPLETE_TALK;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.MainQuestData.TalkData;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.*;
import lombok.NonNull;
import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_COMPLETE_TALK;
import static emu.grasscutter.game.quest.enums.QuestContent.*;
public final class TalkManager extends BasePlayerManager {
public TalkManager(@NonNull Player player) {
super(player);
@@ -22,30 +20,31 @@ public final class TalkManager extends BasePlayerManager {
* @param npcEntityId The entity ID of the NPC being talked to.
*/
public void triggerTalkAction(int talkId, int npcEntityId) {
var talkData = GameData.getTalkConfigDataMap().get(talkId);
if (talkData == null) return;
var player = this.getPlayer();
// Check if the NPC id is valid.
var entity = player.getScene().getEntityById(npcEntityId);
if (entity != null) {
// The config ID of the entity is the NPC's ID.
if (!talkData.getNpcId().contains(entity.getConfigId())) return;
}
// Execute the talk action on associated handlers.
talkData
var talkData = GameData.getTalkConfigDataMap().get(talkId);
if (talkData != null) {
// Check if the NPC id is valid.
var entity = player.getScene().getEntityById(npcEntityId);
if (entity != null) {
// The config ID of the entity is the NPC's ID.
if (!talkData.getNpcId().contains(entity.getConfigId())) return;
}
// Execute the talk action on associated handlers.
talkData
.getFinishExec()
.forEach(e -> player.getServer().getTalkSystem().triggerExec(player, talkData, e));
// Save the talk value to the quest's data.
this.saveTalkToQuest(talkId, talkData.getQuestId());
}
// Invoke the talking events for quests.
var questManager = player.getQuestManager();
questManager.queueEvent(QUEST_CONTENT_COMPLETE_ANY_TALK, talkId);
questManager.queueEvent(QUEST_CONTENT_COMPLETE_TALK, talkId);
questManager.queueEvent(QUEST_COND_COMPLETE_TALK, talkId);
// Save the talk value to the quest's data.
this.saveTalkToQuest(talkId, talkData.getQuestId());
}
public void saveTalkToQuest(int talkId, int mainQuestId) {
@@ -1,54 +1,44 @@
package emu.grasscutter.game.world;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.*;
import emu.grasscutter.data.binout.SceneNpcBornEntry;
import emu.grasscutter.data.binout.routes.Route;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.SceneData;
import emu.grasscutter.data.excels.*;
import emu.grasscutter.data.excels.codex.CodexAnimalData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.data.excels.world.WorldLevelData;
import emu.grasscutter.data.server.Grid;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.dungeons.DungeonSettleListener;
import emu.grasscutter.game.dungeons.*;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.managers.blossom.BlossomManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.TeamInfo;
import emu.grasscutter.game.player.*;
import emu.grasscutter.game.props.*;
import emu.grasscutter.game.quest.QuestGroupSuite;
import emu.grasscutter.game.world.data.TeleportProperties;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.EnterTypeOuterClass;
import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.scripts.SceneScriptManager;
import emu.grasscutter.scripts.*;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneBlock;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.scripts.data.*;
import emu.grasscutter.server.event.entity.EntityCreationEvent;
import emu.grasscutter.server.event.player.PlayerTeleportEvent;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.objects.KahnsSort;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import lombok.*;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public final class Scene {
@Getter private final World world;
@@ -1,17 +1,18 @@
package emu.grasscutter.plugin;
import static emu.grasscutter.utils.lang.Language.translate;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.event.*;
import emu.grasscutter.utils.*;
import lombok.*;
import javax.annotation.Nullable;
import java.io.*;
import java.lang.reflect.Method;
import java.net.*;
import java.util.*;
import java.util.jar.*;
import javax.annotation.Nullable;
import lombok.*;
import static emu.grasscutter.utils.lang.Language.translate;
/** Manages the server's plugins and the event system. */
public final class PluginManager {
@@ -216,9 +217,14 @@ public final class PluginManager {
Grasscutter.getLogger().info(translate("plugin.enabling_plugin", name));
try {
plugin.onEnable();
return;
} catch (NoSuchMethodError ignored) {
Grasscutter.getLogger().error(translate("plugin.invalid_api.outdated", name));
} catch (Throwable exception) {
Grasscutter.getLogger().error(translate("plugin.enabling_failed", name), exception);
}
this.disablePlugin(plugin);
});
}
@@ -1,7 +1,5 @@
package emu.grasscutter.scripts;
import static emu.grasscutter.scripts.constants.EventType.EVENT_TIMER_EVENT;
import com.github.davidmoten.rtreemulti.RTree;
import com.github.davidmoten.rtreemulti.geometry.Geometry;
import emu.grasscutter.Grasscutter;
@@ -21,17 +19,20 @@ import emu.grasscutter.server.packet.send.PacketGroupSuiteNotify;
import emu.grasscutter.utils.*;
import io.netty.util.concurrent.FastThreadLocalThread;
import it.unimi.dsi.fastutil.ints.*;
import kotlin.Pair;
import lombok.val;
import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import javax.annotation.*;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import javax.annotation.*;
import kotlin.Pair;
import lombok.val;
import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import static emu.grasscutter.scripts.constants.EventType.EVENT_TIMER_EVENT;
public class SceneScriptManager {
private final Scene scene;
@@ -801,26 +802,26 @@ public class SceneScriptManager {
private void realCallEvent(@Nonnull ScriptArgs params) {
try {
ScriptLoader.getScriptLib().setSceneScriptManager(this);
int eventType = params.type;
Set<SceneTrigger> relevantTriggers = new HashSet<>();
if (eventType == EventType.EVENT_ENTER_REGION || eventType == EventType.EVENT_LEAVE_REGION) {
relevantTriggers =
this.getTriggersByEvent(eventType).stream()
.filter(
t ->
t.getCondition().contains(String.valueOf(params.param1))
&& (t.getSource().isEmpty()
|| t.getSource().equals(params.getEventSource())))
.collect(Collectors.toSet());
} else {
relevantTriggers =
this.getTriggersByEvent(eventType).stream()
.filter(
t -> params.getGroupId() == 0 || t.getCurrentGroup().id == params.getGroupId())
.filter(
t -> (t.getSource().isEmpty() || t.getSource().equals(params.getEventSource())))
.collect(Collectors.toSet());
}
var eventType = params.type;
var relevantTriggers = switch (eventType) {
case EventType.EVENT_ENTER_REGION, EventType.EVENT_LEAVE_REGION ->
this.getTriggersByEvent(eventType).stream()
.filter(
t ->
t.getCondition().contains(String.valueOf(params.param1))
&& (t.getSource().isEmpty()
|| t.getSource().equals(params.getEventSource())))
.collect(Collectors.toSet());
default ->
this.getTriggersByEvent(eventType).stream()
.filter(
t -> params.getGroupId() == 0 || t.getCurrentGroup().id == params.getGroupId())
.filter(
t -> (t.getSource().isEmpty() || t.getSource().equals(params.getEventSource())))
.collect(Collectors.toSet());
};
for (SceneTrigger trigger : relevantTriggers) {
handleEventForTrigger(params, trigger);
}
@@ -129,7 +129,7 @@ public class ScriptLib {
public int SetWorktopOptionsByGroupId(int groupId, int configId, int[] options) {
logger.debug("[LUA] Call SetWorktopOptionsByGroupId with {},{},{}",
groupId,configId,options);
groupId, configId, options);
val entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, groupId);
@@ -1,8 +1,5 @@
package emu.grasscutter.server.game;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.lang.Language.translate;
import emu.grasscutter.*;
import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.database.DatabaseHelper;
@@ -32,14 +29,19 @@ import emu.grasscutter.server.event.internal.*;
import emu.grasscutter.server.event.types.ServerEvent;
import emu.grasscutter.server.scheduler.ServerTaskScheduler;
import emu.grasscutter.task.TaskMap;
import java.net.*;
import java.time.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import emu.grasscutter.utils.Utils;
import kcp.highway.*;
import lombok.*;
import org.jetbrains.annotations.NotNull;
import java.net.*;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.lang.Language.translate;
@Getter
public final class GameServer extends KcpServer implements Iterable<Player> {
// Game server base
@@ -326,16 +328,27 @@ public final class GameServer extends KcpServer implements Iterable<Player> {
}
public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
var event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call();
this.getPlayers()
.forEach(
(uid, player) -> {
player.getSession().close();
});
.forEach(
(uid, player) -> player.getSession().close());
this.getWorlds().forEach(World::save);
Utils.sleep(1000L); // Wait 1 second for operations to finish.
try {
var threadPool = GameSessionManager.getLogicThread();
// Shutdown network thread.
threadPool.shutdownGracefully();
// Wait for the network thread to finish.
if (!threadPool.awaitTermination(5, TimeUnit.SECONDS)) {
Grasscutter.getLogger().error("Logic thread did not terminate!");
}
} catch (InterruptedException ignored) { }
}
@NotNull @Override
@@ -1,27 +1,21 @@
package emu.grasscutter.server.game;
import static emu.grasscutter.config.Configuration.GAME_INFO;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketOpcodesUtils;
import emu.grasscutter.net.packet.*;
import emu.grasscutter.server.event.game.SendPacketEvent;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import emu.grasscutter.utils.*;
import io.netty.buffer.*;
import lombok.*;
import java.io.File;
import java.net.InetSocketAddress;
import java.nio.file.Path;
import lombok.Getter;
import lombok.Setter;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.lang.Language.translate;
public class GameSession implements GameSessionManager.KcpChannel {
private final GameServer server;
@@ -139,7 +133,11 @@ public class GameSession implements GameSessionManager.KcpChannel {
SendPacketEvent event = new SendPacketEvent(this, packet);
event.call();
if (!event.isCanceled()) { // If event is not cancelled, continue.
tunnel.writeData(event.getPacket().build());
try {
tunnel.writeData(event.getPacket().build());
} catch (Exception ignored) {
Grasscutter.getLogger().debug("Unable to send packet to client.");
}
}
}
@@ -2,16 +2,16 @@ package emu.grasscutter.server.game;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.Utils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.*;
import io.netty.channel.DefaultEventLoop;
import kcp.highway.*;
import lombok.Getter;
import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import kcp.highway.KcpListener;
import kcp.highway.Ukcp;
public class GameSessionManager {
private static final DefaultEventLoop logicThread = new DefaultEventLoop();
@Getter private static final DefaultEventLoop logicThread = new DefaultEventLoop();
private static final ConcurrentHashMap<Ukcp, GameSession> sessions = new ConcurrentHashMap<>();
private static final KcpListener listener =
new KcpListener() {
@@ -1,15 +1,13 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.*;
import emu.grasscutter.net.proto.QuestCreateEntityReqOuterClass.QuestCreateEntityReq;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketQuestCreateEntityRsp;
import lombok.val;
@@ -30,12 +28,29 @@ public class HandlerQuestCreateEntityReq extends PacketHandler {
case GADGET_ID -> {
val gadgetId = entity.getGadgetId();
val gadgetInfo = entity.getGadget();
GadgetData gadgetData = GameData.getGadgetDataMap().get(gadgetId);
var gadgetData = GameData.getGadgetDataMap().get(gadgetId);
gameEntity =
switch (gadgetData.getType()) {
case Vehicle -> new EntityVehicle(scene, session.getPlayer(), gadgetId, 0, pos, rot);
case Chest -> {
var chest = gadgetInfo.getChest();
var gadget = new EntityGadget(scene, gadgetId, pos, rot);
// Create the gadget data for the chest.
var metaGadget = new SceneGadget();
metaGadget.drop_count = 1; // TODO: Check if more items should be dropped.
metaGadget.chest_drop_id = chest.getChestDropId();
metaGadget.setShowcutscene(chest.getIsShowCutscene());
// Apply the gadget data to the chest.
gadget.setMetaGadget(metaGadget);
yield gadget;
}
default -> new EntityGadget(scene, gadgetId, pos, rot);
};
if (gameEntity instanceof EntityGadget gadget) {
gadget.buildContent();
}
}
case ITEM_ID -> {
val itemId = entity.getItemId();
+37 -5
View File
@@ -1,8 +1,5 @@
package emu.grasscutter.utils;
import static emu.grasscutter.utils.FileUtils.getResourcePath;
import static emu.grasscutter.utils.lang.Language.translate;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.config.ConfigContainer;
import emu.grasscutter.data.DataLoader;
@@ -11,15 +8,20 @@ import emu.grasscutter.utils.objects.Returnable;
import io.javalin.http.Context;
import io.netty.buffer.*;
import it.unimi.dsi.fastutil.ints.*;
import org.slf4j.Logger;
import javax.annotation.Nullable;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.time.*;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import static emu.grasscutter.utils.FileUtils.getResourcePath;
import static emu.grasscutter.utils.lang.Language.translate;
@SuppressWarnings({"UnusedReturnValue", "BooleanMethodIsAlwaysInverted"})
public final class Utils {
@@ -484,6 +486,7 @@ public final class Utils {
*
* @param runnable The task to run.
*/
@SuppressWarnings("BusyWait")
public static void waitFor(Returnable<Boolean> runnable) {
while (!runnable.invoke()) {
try {
@@ -493,4 +496,33 @@ public final class Utils {
}
}
}
/**
* Recursively finds all fields in a class.
*
* @param type The class to find fields in.
* @return A list of all fields in the class.
*/
public static List<Field> getAllFields(Class<?> type) {
var fields = new LinkedList<>(Arrays.asList(type.getDeclaredFields()));
// Check for superclasses.
if (type.getSuperclass() != null) {
fields.addAll(getAllFields(type.getSuperclass()));
}
return fields;
}
/**
* Sleeps the current thread without an exception.
*
* @param millis The amount of milliseconds to sleep.
*/
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ignored) {
}
}
}
@@ -1,11 +1,11 @@
package emu.grasscutter.utils.objects;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.*;
import emu.grasscutter.server.dispatch.IDispatcher;
import emu.grasscutter.utils.Utils;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.*;
public interface FieldFetch {
/**
@@ -18,7 +18,7 @@ public interface FieldFetch {
// Prepare field properties.
var fieldValues = new JsonObject();
var fieldMap = new HashMap<String, Field>();
Arrays.stream(this.getClass().getDeclaredFields())
Utils.getAllFields(this.getClass())
.forEach(field -> fieldMap.put(field.getName(), field));
// Find the values of all requested fields.
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "Failed to disable plugin: %s",
"invalid_api": {
"not_present": "Plugin %s does not specify an API version.",
"lower": "Plugin %s is using API version %s, while the server is using API version %s."
"lower": "Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "Error al desactivar el plugin: %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "Impossible de désactiver le plugin %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "Impossibile disabilitare il plug-in: %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "プラグインの無効化に失敗しました: %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "플러그인을 비활성화하는데 실패했습니다: %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "Nie udało się wyłączyć pluginu: %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "🇺🇸Failed to disable plugin: %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "Ошибка отключения Плагина: %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "无法禁用插件:%s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}
+2 -1
View File
@@ -463,7 +463,8 @@
"disabling_failed": "🇺🇸Failed to disable plugin: %s",
"invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s."
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method."
}
}
}