mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-06-26 18:14:50 +08:00
Compare commits
No commits in common. "5b76ef7dfa76aa07cbd15caa7127af56bb9660fa" and "e9b3516854eede069b7b0fafe4b0d0494853e26d" have entirely different histories.
5b76ef7dfa
...
e9b3516854
@ -8,7 +8,6 @@ import emu.grasscutter.auth.DefaultAuthentication;
|
|||||||
import emu.grasscutter.command.CommandMap;
|
import emu.grasscutter.command.CommandMap;
|
||||||
import emu.grasscutter.command.DefaultPermissionHandler;
|
import emu.grasscutter.command.DefaultPermissionHandler;
|
||||||
import emu.grasscutter.command.PermissionHandler;
|
import emu.grasscutter.command.PermissionHandler;
|
||||||
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
|
||||||
import emu.grasscutter.game.managers.energy.EnergyManager;
|
import emu.grasscutter.game.managers.energy.EnergyManager;
|
||||||
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
||||||
import emu.grasscutter.plugin.PluginManager;
|
import emu.grasscutter.plugin.PluginManager;
|
||||||
@ -114,7 +113,6 @@ public final class Grasscutter {
|
|||||||
ResourceLoader.loadAll();
|
ResourceLoader.loadAll();
|
||||||
ScriptLoader.init();
|
ScriptLoader.init();
|
||||||
EnergyManager.initialize();
|
EnergyManager.initialize();
|
||||||
DungeonChallenge.initialize();
|
|
||||||
|
|
||||||
// Initialize database.
|
// Initialize database.
|
||||||
DatabaseManager.initialize();
|
DatabaseManager.initialize();
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package emu.grasscutter.game.dungeons;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DungeonDrop {
|
|
||||||
private int dungeonId;
|
|
||||||
private List<DungeonDropEntry> drops;
|
|
||||||
|
|
||||||
public int getDungeonId() {
|
|
||||||
return dungeonId;
|
|
||||||
}
|
|
||||||
public void setDungeonId(int dungeonId) {
|
|
||||||
this.dungeonId = dungeonId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DungeonDropEntry> getDrops() {
|
|
||||||
return drops;
|
|
||||||
}
|
|
||||||
public void setDrops(List<DungeonDropEntry> drops) {
|
|
||||||
this.drops = drops;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package emu.grasscutter.game.dungeons;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DungeonDropEntry {
|
|
||||||
private List<Integer> counts;
|
|
||||||
private List<Integer> items;
|
|
||||||
private List<Integer> probabilities;
|
|
||||||
private List<Integer> itemProbabilities;
|
|
||||||
private boolean mpDouble;
|
|
||||||
|
|
||||||
public List<Integer> getCounts() {
|
|
||||||
return counts;
|
|
||||||
}
|
|
||||||
public void setCounts(List<Integer> counts) {
|
|
||||||
this.counts = counts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Integer> getItems() {
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
public void setItems(List<Integer> items) {
|
|
||||||
this.items = items;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Integer> getProbabilities() {
|
|
||||||
return probabilities;
|
|
||||||
}
|
|
||||||
public void setProbabilities(List<Integer> probabilities) {
|
|
||||||
this.probabilities = probabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Integer> getItemProbabilities() {
|
|
||||||
return itemProbabilities;
|
|
||||||
}
|
|
||||||
public void setItemProbabilities(List<Integer> itemProbabilities) {
|
|
||||||
this.itemProbabilities = itemProbabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMpDouble() {
|
|
||||||
return mpDouble;
|
|
||||||
}
|
|
||||||
public void setMpDouble(boolean mpDouble) {
|
|
||||||
this.mpDouble = mpDouble;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +1,21 @@
|
|||||||
package emu.grasscutter.game.dungeons.challenge;
|
package emu.grasscutter.game.dungeons.challenge;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.data.DataLoader;
|
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.data.excels.DungeonData;
|
import emu.grasscutter.data.excels.DungeonData;
|
||||||
import emu.grasscutter.game.dungeons.DungeonDrop;
|
|
||||||
import emu.grasscutter.game.dungeons.DungeonDropEntry;
|
|
||||||
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||||
import emu.grasscutter.game.inventory.GameItem;
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
import emu.grasscutter.game.inventory.ItemType;
|
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||||
import emu.grasscutter.utils.Utils;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
|
|
||||||
public class DungeonChallenge extends WorldChallenge {
|
public class DungeonChallenge extends WorldChallenge {
|
||||||
|
|
||||||
@ -41,24 +25,6 @@ public class DungeonChallenge extends WorldChallenge {
|
|||||||
private boolean stage;
|
private boolean stage;
|
||||||
private IntSet rewardedPlayers;
|
private IntSet rewardedPlayers;
|
||||||
|
|
||||||
private final static Int2ObjectMap<List<DungeonDropEntry>> dungeonDropData = new Int2ObjectOpenHashMap<>();
|
|
||||||
|
|
||||||
public static void initialize() {
|
|
||||||
// Read the data we need for dungeon rewards drops.
|
|
||||||
try (Reader fileReader = new InputStreamReader(DataLoader.load("DungeonDrop.json"))) {
|
|
||||||
List<DungeonDrop> dungeonDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DungeonDrop.class).getType());
|
|
||||||
|
|
||||||
for (DungeonDrop entry : dungeonDropList) {
|
|
||||||
dungeonDropData.put(entry.getDungeonId(), entry.getDrops());
|
|
||||||
}
|
|
||||||
|
|
||||||
Grasscutter.getLogger().info("Loaded {} dungeon drop data entries.", dungeonDropData.size());
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
Grasscutter.getLogger().error("Unable to load dungeon drop data.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DungeonChallenge(Scene scene, SceneGroup group,
|
public DungeonChallenge(Scene scene, SceneGroup group,
|
||||||
int challengeId, int challengeIndex,
|
int challengeId, int challengeIndex,
|
||||||
List<Integer> paramList,
|
List<Integer> paramList,
|
||||||
@ -101,64 +67,8 @@ public class DungeonChallenge extends WorldChallenge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<GameItem> rollRewards(boolean useCondensed) {
|
public void getStatueDrops(Player player) {
|
||||||
List<GameItem> rewards = new ArrayList<>();
|
|
||||||
int dungeonId = this.getScene().getDungeonData().getId();
|
|
||||||
// If we have specific drop data for this dungeon, we use it.
|
|
||||||
if (dungeonDropData.containsKey(dungeonId)) {
|
|
||||||
List<DungeonDropEntry> dropEntries = dungeonDropData.get(dungeonId);
|
|
||||||
|
|
||||||
// Roll for each drop group.
|
|
||||||
for (var entry : dropEntries) {
|
|
||||||
// Determine the number of drops we get for this entry.
|
|
||||||
int start = entry.getCounts().get(0);
|
|
||||||
int end = entry.getCounts().get(entry.getCounts().size() - 1);
|
|
||||||
var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
|
|
||||||
|
|
||||||
int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
|
||||||
|
|
||||||
if (useCondensed) {
|
|
||||||
amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double rewards in multiplay mode, if specified.
|
|
||||||
if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
|
|
||||||
amount *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Roll items for this group.
|
|
||||||
// Here, we have to handle stacking, or the client will not display results correctly.
|
|
||||||
// For now, we use the following logic: If the possible drop item are a list of multiple items,
|
|
||||||
// we roll them separately. If not, we stack them. This should work out in practice, at least
|
|
||||||
// for the currently existing set of dungeons.
|
|
||||||
if (entry.getItems().size() == 1) {
|
|
||||||
rewards.add(new GameItem(entry.getItems().get(0), amount));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int i = 0; i < amount; i++) {
|
|
||||||
// int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size());
|
|
||||||
// int itemId = entry.getItems().get(itemIndex);
|
|
||||||
int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
|
|
||||||
rewards.add(new GameItem(itemId, 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Otherwise, we fall back to the preview data.
|
|
||||||
else {
|
|
||||||
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
|
|
||||||
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
|
||||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rewards;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getStatueDrops(Player player, GadgetInteractReq request) {
|
|
||||||
DungeonData dungeonData = getScene().getDungeonData();
|
DungeonData dungeonData = getScene().getDungeonData();
|
||||||
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
|
||||||
|
|
||||||
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -168,42 +78,11 @@ public class DungeonChallenge extends WorldChallenge {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get rewards.
|
|
||||||
List<GameItem> rewards = new ArrayList<>();
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
|
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||||
if (request.getIsUseCondenseResin()) {
|
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||||
// Check if condensed resin is usable here.
|
|
||||||
// For this, we use the following logic for now:
|
|
||||||
// The normal resin cost of the dungeon has to be 20.
|
|
||||||
if (resinCost != 20) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the player has condensed resin.
|
|
||||||
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
|
|
||||||
if (condensedResin == null || condensedResin.getCount() <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deduct.
|
|
||||||
player.getInventory().removeItem(condensedResin, 1);
|
|
||||||
|
|
||||||
// Roll rewards.
|
|
||||||
rewards.addAll(this.rollRewards(true));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// If the player used regular resin, try to deduct.
|
|
||||||
// Stop if insufficient resin.
|
|
||||||
boolean success = player.getResinManager().useResin(resinCost);
|
|
||||||
if (!success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Roll rewards.
|
|
||||||
rewards.addAll(this.rollRewards(false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add rewards to player and send notification.
|
|
||||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import emu.grasscutter.Grasscutter;
|
|||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo;
|
import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
@ -18,7 +18,7 @@ public class GadgetChest extends GadgetContent {
|
|||||||
super(gadget);
|
super(gadget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
public boolean onInteract(Player player, InterOpType opType) {
|
||||||
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestInteractHandlerMap();
|
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestInteractHandlerMap();
|
||||||
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
|
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
|
||||||
if(handler == null){
|
if(handler == null){
|
||||||
@ -26,7 +26,7 @@ public class GadgetChest extends GadgetContent {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(req.getOpType() == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()){
|
if(opType == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()){
|
||||||
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
|
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
|
||||||
return false;
|
return false;
|
||||||
}else{
|
}else{
|
||||||
|
@ -3,7 +3,6 @@ package emu.grasscutter.game.entity.gadget;
|
|||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
|
||||||
public abstract class GadgetContent {
|
public abstract class GadgetContent {
|
||||||
@ -17,7 +16,7 @@ public abstract class GadgetContent {
|
|||||||
return gadget;
|
return gadget;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean onInteract(Player player, GadgetInteractReq req);
|
public abstract boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType);
|
||||||
|
|
||||||
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
|
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import emu.grasscutter.game.inventory.GameItem;
|
|||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
|
||||||
public class GadgetGatherPoint extends GadgetContent {
|
public class GadgetGatherPoint extends GadgetContent {
|
||||||
@ -26,7 +26,7 @@ public class GadgetGatherPoint extends GadgetContent {
|
|||||||
return getGatherData().getItemId();
|
return getGatherData().getItemId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
GameItem item = new GameItem(gatherData.getItemId(), 1);
|
GameItem item = new GameItem(gatherData.getItemId(), 1);
|
||||||
|
|
||||||
player.getInventory().addItem(item, ActionReason.Gather);
|
player.getInventory().addItem(item, ActionReason.Gather);
|
||||||
|
@ -3,7 +3,7 @@ package emu.grasscutter.game.entity.gadget;
|
|||||||
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||||
@ -14,9 +14,9 @@ public class GadgetRewardStatue extends GadgetContent {
|
|||||||
super(gadget);
|
super(gadget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
|
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
|
||||||
dungeonChallenge.getStatueDrops(player, req);
|
dungeonChallenge.getStatueDrops(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE));
|
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE));
|
||||||
|
@ -4,7 +4,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
@ -35,7 +35,7 @@ public class GadgetWorktop extends GadgetContent {
|
|||||||
this.worktopOptions.remove(option);
|
this.worktopOptions.remove(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -990,7 +990,7 @@ public class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void interactWith(int gadgetEntityId, GadgetInteractReq req) {
|
public void interactWith(int gadgetEntityId, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
return;
|
return;
|
||||||
@ -1023,7 +1023,7 @@ public class Player {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean shouldDelete = gadget.getContent().onInteract(this, req);
|
boolean shouldDelete = gadget.getContent().onInteract(this, opType);
|
||||||
|
|
||||||
if (shouldDelete) {
|
if (shouldDelete) {
|
||||||
entity.getScene().removeEntity(entity);
|
entity.getScene().removeEntity(entity);
|
||||||
|
@ -1,23 +1,9 @@
|
|||||||
package emu.grasscutter.server.event.game;
|
package emu.grasscutter.server.event.game;
|
||||||
|
|
||||||
import emu.grasscutter.server.event.types.ServerEvent;
|
import emu.grasscutter.server.event.types.ServerEvent;
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
public final class ServerTickEvent extends ServerEvent {
|
public final class ServerTickEvent extends ServerEvent {
|
||||||
private final Instant start, end;
|
public ServerTickEvent() {
|
||||||
|
|
||||||
public ServerTickEvent(Instant start, Instant end) {
|
|
||||||
super(Type.GAME);
|
super(Type.GAME);
|
||||||
|
|
||||||
this.start = start;
|
|
||||||
this.end = end;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public Instant getTickStart() {
|
|
||||||
return this.start;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instant getTickEnd() {
|
|
||||||
return this.end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -26,14 +26,11 @@ import emu.grasscutter.server.event.types.ServerEvent;
|
|||||||
import emu.grasscutter.server.event.game.ServerTickEvent;
|
import emu.grasscutter.server.event.game.ServerTickEvent;
|
||||||
import emu.grasscutter.server.event.internal.ServerStartEvent;
|
import emu.grasscutter.server.event.internal.ServerStartEvent;
|
||||||
import emu.grasscutter.server.event.internal.ServerStopEvent;
|
import emu.grasscutter.server.event.internal.ServerStopEvent;
|
||||||
import emu.grasscutter.server.scheduler.ServerTaskScheduler;
|
|
||||||
import emu.grasscutter.task.TaskMap;
|
import emu.grasscutter.task.TaskMap;
|
||||||
import kcp.highway.ChannelConfig;
|
import kcp.highway.ChannelConfig;
|
||||||
import kcp.highway.KcpServer;
|
import kcp.highway.KcpServer;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -45,11 +42,10 @@ public final class GameServer extends KcpServer {
|
|||||||
private final InetSocketAddress address;
|
private final InetSocketAddress address;
|
||||||
private final GameServerPacketHandler packetHandler;
|
private final GameServerPacketHandler packetHandler;
|
||||||
private final ServerQuestHandler questHandler;
|
private final ServerQuestHandler questHandler;
|
||||||
@Getter private final ServerTaskScheduler scheduler;
|
|
||||||
|
|
||||||
private final Map<Integer, Player> players;
|
private final Map<Integer, Player> players;
|
||||||
private final Set<World> worlds;
|
private final Set<World> worlds;
|
||||||
|
|
||||||
private ChatManagerHandler chatManager;
|
private ChatManagerHandler chatManager;
|
||||||
private final InventoryManager inventoryManager;
|
private final InventoryManager inventoryManager;
|
||||||
private final GachaManager gachaManager;
|
private final GachaManager gachaManager;
|
||||||
@ -61,14 +57,14 @@ public final class GameServer extends KcpServer {
|
|||||||
private final TaskMap taskMap;
|
private final TaskMap taskMap;
|
||||||
private final DropManager dropManager;
|
private final DropManager dropManager;
|
||||||
private final WorldDataManager worldDataManager;
|
private final WorldDataManager worldDataManager;
|
||||||
|
|
||||||
private final CombineManger combineManger;
|
private final CombineManger combineManger;
|
||||||
private final TowerScheduleManager towerScheduleManager;
|
private final TowerScheduleManager towerScheduleManager;
|
||||||
|
|
||||||
public GameServer() {
|
public GameServer() {
|
||||||
this(getAdapterInetSocketAddress());
|
this(getAdapterInetSocketAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameServer(InetSocketAddress address) {
|
public GameServer(InetSocketAddress address) {
|
||||||
ChannelConfig channelConfig = new ChannelConfig();
|
ChannelConfig channelConfig = new ChannelConfig();
|
||||||
channelConfig.nodelay(true,40,2,true);
|
channelConfig.nodelay(true,40,2,true);
|
||||||
@ -84,10 +80,9 @@ public final class GameServer extends KcpServer {
|
|||||||
this.address = address;
|
this.address = address;
|
||||||
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
||||||
this.questHandler = new ServerQuestHandler();
|
this.questHandler = new ServerQuestHandler();
|
||||||
this.scheduler = new ServerTaskScheduler();
|
|
||||||
this.players = new ConcurrentHashMap<>();
|
this.players = new ConcurrentHashMap<>();
|
||||||
this.worlds = Collections.synchronizedSet(new HashSet<>());
|
this.worlds = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
this.chatManager = new ChatManager(this);
|
this.chatManager = new ChatManager(this);
|
||||||
this.inventoryManager = new InventoryManager(this);
|
this.inventoryManager = new InventoryManager(this);
|
||||||
this.gachaManager = new GachaManager(this);
|
this.gachaManager = new GachaManager(this);
|
||||||
@ -104,7 +99,7 @@ public final class GameServer extends KcpServer {
|
|||||||
// Hook into shutdown event.
|
// Hook into shutdown event.
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameServerPacketHandler getPacketHandler() {
|
public GameServerPacketHandler getPacketHandler() {
|
||||||
return packetHandler;
|
return packetHandler;
|
||||||
}
|
}
|
||||||
@ -124,7 +119,7 @@ public final class GameServer extends KcpServer {
|
|||||||
public ChatManagerHandler getChatManager() {
|
public ChatManagerHandler getChatManager() {
|
||||||
return chatManager;
|
return chatManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setChatManager(ChatManagerHandler chatManager) {
|
public void setChatManager(ChatManagerHandler chatManager) {
|
||||||
this.chatManager = chatManager;
|
this.chatManager = chatManager;
|
||||||
}
|
}
|
||||||
@ -136,7 +131,7 @@ public final class GameServer extends KcpServer {
|
|||||||
public GachaManager getGachaManager() {
|
public GachaManager getGachaManager() {
|
||||||
return gachaManager;
|
return gachaManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShopManager getShopManager() {
|
public ShopManager getShopManager() {
|
||||||
return shopManager;
|
return shopManager;
|
||||||
}
|
}
|
||||||
@ -148,7 +143,7 @@ public final class GameServer extends KcpServer {
|
|||||||
public DropManager getDropManager() {
|
public DropManager getDropManager() {
|
||||||
return dropManager;
|
return dropManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DungeonManager getDungeonManager() {
|
public DungeonManager getDungeonManager() {
|
||||||
return dungeonManager;
|
return dungeonManager;
|
||||||
}
|
}
|
||||||
@ -176,7 +171,7 @@ public final class GameServer extends KcpServer {
|
|||||||
public TaskMap getTaskMap() {
|
public TaskMap getTaskMap() {
|
||||||
return this.taskMap;
|
return this.taskMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InetSocketAddress getAdapterInetSocketAddress(){
|
private static InetSocketAddress getAdapterInetSocketAddress(){
|
||||||
InetSocketAddress inetSocketAddress;
|
InetSocketAddress inetSocketAddress;
|
||||||
if(GAME_INFO.bindAddress.equals("")){
|
if(GAME_INFO.bindAddress.equals("")){
|
||||||
@ -189,7 +184,7 @@ public final class GameServer extends KcpServer {
|
|||||||
}
|
}
|
||||||
return inetSocketAddress;
|
return inetSocketAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerPlayer(Player player) {
|
public void registerPlayer(Player player) {
|
||||||
getPlayers().put(player.getUid(), player);
|
getPlayers().put(player.getUid(), player);
|
||||||
}
|
}
|
||||||
@ -197,44 +192,44 @@ public final class GameServer extends KcpServer {
|
|||||||
public Player getPlayerByUid(int id) {
|
public Player getPlayerByUid(int id) {
|
||||||
return this.getPlayerByUid(id, false);
|
return this.getPlayerByUid(id, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
|
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
|
||||||
// Console check
|
// Console check
|
||||||
if (id == GameConstants.SERVER_CONSOLE_UID) {
|
if (id == GameConstants.SERVER_CONSOLE_UID) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get from online players
|
// Get from online players
|
||||||
Player player = this.getPlayers().get(id);
|
Player player = this.getPlayers().get(id);
|
||||||
|
|
||||||
if (!allowOfflinePlayers) {
|
if (!allowOfflinePlayers) {
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check database if character isnt here
|
// Check database if character isnt here
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
player = DatabaseHelper.getPlayerByUid(id);
|
player = DatabaseHelper.getPlayerByUid(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayerByAccountId(String accountId) {
|
public Player getPlayerByAccountId(String accountId) {
|
||||||
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
|
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
|
||||||
return playerOpt.orElse(null);
|
return playerOpt.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocialDetail.Builder getSocialDetailByUid(int id) {
|
public SocialDetail.Builder getSocialDetailByUid(int id) {
|
||||||
// Get from online players
|
// Get from online players
|
||||||
Player player = this.getPlayerByUid(id, true);
|
Player player = this.getPlayerByUid(id, true);
|
||||||
|
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return player.getSocialDetail();
|
return player.getSocialDetail();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getAccountByName(String username) {
|
public Account getAccountByName(String username) {
|
||||||
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
|
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
|
||||||
if (playerOpt.isPresent()) {
|
if (playerOpt.isPresent()) {
|
||||||
@ -243,41 +238,32 @@ public final class GameServer extends KcpServer {
|
|||||||
return DatabaseHelper.getAccountByName(username);
|
return DatabaseHelper.getAccountByName(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void onTick() {
|
public synchronized void onTick(){
|
||||||
var tickStart = Instant.now();
|
Iterator<World> it = this.getWorlds().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
World world = it.next();
|
||||||
|
|
||||||
// Tick worlds.
|
if (world.getPlayerCount() == 0) {
|
||||||
Iterator<World> it = this.getWorlds().iterator();
|
it.remove();
|
||||||
while (it.hasNext()) {
|
}
|
||||||
World world = it.next();
|
|
||||||
|
|
||||||
if (world.getPlayerCount() == 0) {
|
world.onTick();
|
||||||
it.remove();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
world.onTick();
|
for (Player player : this.getPlayers().values()) {
|
||||||
}
|
player.onTick();
|
||||||
|
}
|
||||||
// Tick players.
|
|
||||||
for (Player player : this.getPlayers().values()) {
|
|
||||||
player.onTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tick scheduler.
|
|
||||||
this.getScheduler().runTasks();
|
|
||||||
|
|
||||||
// Call server tick event.
|
|
||||||
ServerTickEvent event = new ServerTickEvent(tickStart, Instant.now());
|
|
||||||
event.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ServerTickEvent event = new ServerTickEvent(); event.call();
|
||||||
|
}
|
||||||
|
|
||||||
public void registerWorld(World world) {
|
public void registerWorld(World world) {
|
||||||
this.getWorlds().add(world);
|
this.getWorlds().add(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deregisterWorld(World world) {
|
public void deregisterWorld(World world) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
@ -13,7 +13,7 @@ public class HandlerGadgetInteractReq extends PacketHandler {
|
|||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
|
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
|
||||||
|
|
||||||
session.getPlayer().interactWith(req.getGadgetEntityId(), req);
|
session.getPlayer().interactWith(req.getGadgetEntityId(), req.getOpType());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
package emu.grasscutter.server.scheduler;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A server task that should be run asynchronously.
|
|
||||||
*/
|
|
||||||
public final class AsyncServerTask implements Runnable {
|
|
||||||
/* The runnable to run. */
|
|
||||||
private final Runnable task;
|
|
||||||
/* This ID is assigned by the scheduler. */
|
|
||||||
@Getter private final int taskId;
|
|
||||||
/* The result callback to run. */
|
|
||||||
@Nullable private final Runnable callback;
|
|
||||||
|
|
||||||
/* Has the task already been started? */
|
|
||||||
private boolean started = false;
|
|
||||||
/* Has the task finished execution? */
|
|
||||||
private boolean finished = false;
|
|
||||||
/* The result produced in the async task. */
|
|
||||||
@Nullable private Object result = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For tasks without a callback.
|
|
||||||
* @param task The task to run.
|
|
||||||
*/
|
|
||||||
public AsyncServerTask(Runnable task, int taskId) {
|
|
||||||
this(task, null, taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For tasks with a callback.
|
|
||||||
* @param task The task to run.
|
|
||||||
* @param callback The task to run after the task is complete.
|
|
||||||
*/
|
|
||||||
public AsyncServerTask(Runnable task, @Nullable Runnable callback, int taskId) {
|
|
||||||
this.task = task;
|
|
||||||
this.callback = callback;
|
|
||||||
this.taskId = taskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the state of the task.
|
|
||||||
* @return True if the task has been started, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean hasStarted() {
|
|
||||||
return this.started;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the state of the task.
|
|
||||||
* @return True if the task has finished execution, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean isFinished() {
|
|
||||||
return this.finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the task.
|
|
||||||
*/
|
|
||||||
@Override public void run() {
|
|
||||||
// Declare the task as started.
|
|
||||||
this.started = true;
|
|
||||||
|
|
||||||
// Run the runnable.
|
|
||||||
this.task.run();
|
|
||||||
|
|
||||||
// Declare the task as finished.
|
|
||||||
this.finished = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the callback.
|
|
||||||
*/
|
|
||||||
public void complete() {
|
|
||||||
// Run the callback.
|
|
||||||
if(this.callback != null)
|
|
||||||
this.callback.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the result of the async task.
|
|
||||||
* @param result The result of the async task.
|
|
||||||
*/
|
|
||||||
public void setResult(@Nullable Object result) {
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the set result of the async task.
|
|
||||||
* @return The result, or null if it has not been set.
|
|
||||||
*/
|
|
||||||
@Nullable public Object getResult() {
|
|
||||||
return this.result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
package emu.grasscutter.server.scheduler;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import lombok.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class works the same as a runnable, except with more information.
|
|
||||||
*/
|
|
||||||
public final class ServerTask implements Runnable {
|
|
||||||
/* The runnable to run. */
|
|
||||||
private final Runnable runnable;
|
|
||||||
/* This ID is assigned by the scheduler. */
|
|
||||||
@Getter private final int taskId;
|
|
||||||
/* The period at which the task should be run. */
|
|
||||||
/* The delay between the first execute. */
|
|
||||||
private final int period, delay;
|
|
||||||
|
|
||||||
public ServerTask(Runnable runnable, int taskId, int period, int delay) {
|
|
||||||
this.runnable = runnable;
|
|
||||||
this.taskId = taskId;
|
|
||||||
this.period = period;
|
|
||||||
this.delay = delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The amount of times the task has been run. */
|
|
||||||
@Getter private int ticks = 0;
|
|
||||||
/* Should the check consider delay? */
|
|
||||||
private boolean considerDelay = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the task from running the next time.
|
|
||||||
*/
|
|
||||||
public void cancel() {
|
|
||||||
Grasscutter.getGameServer().getScheduler().cancelTask(this.taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the task should run at the current tick.
|
|
||||||
* @return True if the task should run, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean shouldRun() {
|
|
||||||
if(this.delay != -1 && this.considerDelay) {
|
|
||||||
this.considerDelay = false;
|
|
||||||
return this.ticks == this.delay;
|
|
||||||
} else if(this.period != -1)
|
|
||||||
return this.ticks % this.period == 0;
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the task should be canceled.
|
|
||||||
* @return True if the task should be canceled, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean shouldCancel() {
|
|
||||||
return this.period == -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the task.
|
|
||||||
*/
|
|
||||||
@Override public void run() {
|
|
||||||
// Run the runnable.
|
|
||||||
this.runnable.run();
|
|
||||||
// Increase tick count.
|
|
||||||
this.ticks++;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
package emu.grasscutter.server.scheduler;
|
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class to manage all time-based tasks scheduled on the server.
|
|
||||||
* This handles both synchronous and asynchronous tasks.
|
|
||||||
*
|
|
||||||
* Developers note: A server tick is ONE REAL-TIME SECOND.
|
|
||||||
*/
|
|
||||||
public final class ServerTaskScheduler {
|
|
||||||
/* A map to contain all running tasks. */
|
|
||||||
private final ConcurrentHashMap<Integer, ServerTask> tasks
|
|
||||||
= new ConcurrentHashMap<>();
|
|
||||||
/* A map to contain all async tasks. */
|
|
||||||
private final ConcurrentHashMap<Integer, AsyncServerTask> asyncTasks
|
|
||||||
= new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/* The ID assigned to the next runnable. */
|
|
||||||
private int nextTaskId = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ran every server tick.
|
|
||||||
* Attempts to run all scheduled tasks.
|
|
||||||
* This method is synchronous and will block until all tasks are complete.
|
|
||||||
*/
|
|
||||||
public void runTasks() {
|
|
||||||
// Skip if there are no tasks.
|
|
||||||
if(this.tasks.size() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Run all tasks.
|
|
||||||
for(ServerTask task : this.tasks.values()) {
|
|
||||||
// Check if the task should run.
|
|
||||||
if (task.shouldRun()) {
|
|
||||||
// Run the task.
|
|
||||||
task.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the task should be canceled.
|
|
||||||
if (task.shouldCancel()) {
|
|
||||||
// Cancel the task.
|
|
||||||
this.cancelTask(task.getTaskId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run all async tasks.
|
|
||||||
for(AsyncServerTask task : this.asyncTasks.values()) {
|
|
||||||
if(!task.hasStarted()) {
|
|
||||||
// Create a thread for the task.
|
|
||||||
Thread thread = new Thread(task);
|
|
||||||
// Start the thread.
|
|
||||||
thread.start();
|
|
||||||
} else if(task.isFinished()) {
|
|
||||||
// Cancel the task.
|
|
||||||
this.asyncTasks.remove(task.getTaskId());
|
|
||||||
// Run the task's callback.
|
|
||||||
task.complete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a task from the scheduler.
|
|
||||||
* @param taskId The ID of the task to get.
|
|
||||||
* @return The task, or null if it does not exist.
|
|
||||||
*/
|
|
||||||
public ServerTask getTask(int taskId) {
|
|
||||||
return this.tasks.get(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an async task from the scheduler.
|
|
||||||
* @param taskId The ID of the task to get.
|
|
||||||
* @return The task, or null if it does not exist.
|
|
||||||
*/
|
|
||||||
public AsyncServerTask getAsyncTask(int taskId) {
|
|
||||||
return this.asyncTasks.get(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a task from the scheduler.
|
|
||||||
* @param taskId The ID of the task to remove.
|
|
||||||
*/
|
|
||||||
public void cancelTask(int taskId) {
|
|
||||||
this.tasks.remove(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules a task to be run on a separate thread.
|
|
||||||
* The task runs on the next server tick.
|
|
||||||
* @param runnable The runnable to run.
|
|
||||||
* @return The ID of the task.
|
|
||||||
*/
|
|
||||||
public int scheduleAsyncTask(Runnable runnable) {
|
|
||||||
// Get the next task ID.
|
|
||||||
var taskId = this.nextTaskId++;
|
|
||||||
// Create a new task.
|
|
||||||
this.asyncTasks.put(taskId, new AsyncServerTask(runnable, taskId));
|
|
||||||
// Return the task ID.
|
|
||||||
return taskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules a task to be run on the next server tick.
|
|
||||||
* @param runnable The runnable to run.
|
|
||||||
* @return The ID of the task.
|
|
||||||
*/
|
|
||||||
public int scheduleTask(Runnable runnable) {
|
|
||||||
return this.scheduleDelayedRepeatingTask(runnable, -1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules a task to be run after the amount of ticks has passed.
|
|
||||||
* @param runnable The runnable to run.
|
|
||||||
* @param delay The amount of ticks to wait before running.
|
|
||||||
* @return The ID of the task.
|
|
||||||
*/
|
|
||||||
public int scheduleDelayedTask(Runnable runnable, int delay) {
|
|
||||||
return this.scheduleDelayedRepeatingTask(runnable, -1, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules a task to be run every amount of ticks.
|
|
||||||
* @param runnable The runnable to run.
|
|
||||||
* @param period The amount of ticks to wait before running again.
|
|
||||||
* @return The ID of the task.
|
|
||||||
*/
|
|
||||||
public int scheduleRepeatingTask(Runnable runnable, int period) {
|
|
||||||
return this.scheduleDelayedRepeatingTask(runnable, period, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedules a task to be run after the amount of ticks has passed.
|
|
||||||
* @param runnable The runnable to run.
|
|
||||||
* @param period The amount of ticks to wait before running again.
|
|
||||||
* @param delay The amount of ticks to wait before running the first time.
|
|
||||||
* @return The ID of the task.
|
|
||||||
*/
|
|
||||||
public int scheduleDelayedRepeatingTask(Runnable runnable, int period, int delay) {
|
|
||||||
// Get the next task ID.
|
|
||||||
var taskId = this.nextTaskId++;
|
|
||||||
// Create a new task.
|
|
||||||
this.tasks.put(taskId, new ServerTask(runnable, taskId, period, delay));
|
|
||||||
// Return the task ID.
|
|
||||||
return taskId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,6 @@ import java.nio.file.StandardCopyOption;
|
|||||||
import java.time.*;
|
import java.time.*;
|
||||||
import java.time.temporal.TemporalAdjusters;
|
import java.time.temporal.TemporalAdjusters;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.DataLoader;
|
import emu.grasscutter.data.DataLoader;
|
||||||
@ -62,7 +61,7 @@ public final class Utils {
|
|||||||
BufferedInputStream bis = new BufferedInputStream(inputStream);
|
BufferedInputStream bis = new BufferedInputStream(inputStream);
|
||||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
for (int result = bis.read(); result != -1; result = bis.read()) {
|
for (int result = bis.read(); result != -1; result = bis.read()) {
|
||||||
buf.write((byte) result);
|
buf.write((byte) result);
|
||||||
}
|
}
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
@ -76,17 +75,17 @@ public final class Utils {
|
|||||||
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
|
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
|
||||||
public static String bytesToHex(byte[] bytes) {
|
public static String bytesToHex(byte[] bytes) {
|
||||||
if (bytes == null) return "";
|
if (bytes == null) return "";
|
||||||
char[] hexChars = new char[bytes.length * 2];
|
char[] hexChars = new char[bytes.length * 2];
|
||||||
for (int j = 0; j < bytes.length; j++) {
|
for (int j = 0; j < bytes.length; j++) {
|
||||||
int v = bytes[j] & 0xFF;
|
int v = bytes[j] & 0xFF;
|
||||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||||
}
|
}
|
||||||
return new String(hexChars);
|
return new String(hexChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String bytesToHex(ByteBuf buf) {
|
public static String bytesToHex(ByteBuf buf) {
|
||||||
return bytesToHex(byteBufToArray(buf));
|
return bytesToHex(byteBufToArray(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] byteBufToArray(ByteBuf buf) {
|
public static byte[] byteBufToArray(ByteBuf buf) {
|
||||||
@ -98,10 +97,10 @@ public final class Utils {
|
|||||||
public static int abilityHash(String str) {
|
public static int abilityHash(String str) {
|
||||||
int v7 = 0;
|
int v7 = 0;
|
||||||
int v8 = 0;
|
int v8 = 0;
|
||||||
while (v8 < str.length()) {
|
while (v8 < str.length()) {
|
||||||
v7 = str.charAt(v8++) + 131 * v7;
|
v7 = str.charAt(v8++) + 131 * v7;
|
||||||
}
|
}
|
||||||
return v7;
|
return v7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -378,35 +377,4 @@ public final class Utils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
* Draws a random element from the given list, following the given probability distribution, if given.
|
|
||||||
* @param list The list from which to draw the element.
|
|
||||||
* @param probabilities The probability distribution. This is given as a list of probabilities of the same length it `list`.
|
|
||||||
* @return A randomly drawn element from the given list.
|
|
||||||
*/
|
|
||||||
public static <T> T drawRandomListElement(List<T> list, List<Integer> probabilities) {
|
|
||||||
// If we don't have a probability distribution, or the size of the distribution does not match
|
|
||||||
// the size of the list, we assume uniform distribution.
|
|
||||||
if (probabilities == null || probabilities.size() <= 1 || probabilities.size() != list.size()) {
|
|
||||||
int index = ThreadLocalRandom.current().nextInt(0, list.size());
|
|
||||||
return list.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we roll with the given distribution.
|
|
||||||
int totalProbabilityMass = probabilities.stream().reduce(Integer::sum).get();
|
|
||||||
int roll = ThreadLocalRandom.current().nextInt(1, totalProbabilityMass + 1);
|
|
||||||
|
|
||||||
int currentTotalChance = 0;
|
|
||||||
for (int i = 0; i < list.size(); i++) {
|
|
||||||
currentTotalChance += probabilities.get(i);
|
|
||||||
|
|
||||||
if (roll <= currentTotalChance) {
|
|
||||||
return list.get(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should never happen.
|
|
||||||
return list.get(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user