Improve Advancements performance

This commit is contained in:
2026-06-12 14:19:47 +02:00
parent 9e43d5cd1b
commit 914134600e
3 changed files with 159 additions and 63 deletions
@@ -29,30 +29,32 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import de.steamwar.messages.Chatter;
import de.steamwar.sql.EventFight;
import de.steamwar.sql.SteamwarUser;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import net.kyori.adventure.text.Component;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
@RequiredArgsConstructor
public class Advancement {
protected static final Map<SteamwarUser, Map<Advancement.Value.Key, Advancement.Value>> values = new HashMap<>();
{
Advancements.all.add(this);
}
protected final Map<SteamwarUser, Advancement.Data> data = new HashMap<>();
protected final Map<SteamwarUser, Advancement.Value> value = new HashMap<>();
public Advancement.Data get(SteamwarUser user) {
return get(user, Data::new);
@@ -143,6 +145,46 @@ public class Advancement {
public abstract boolean hidden(Data data);
}
public static class Value<T extends Number> {
@AllArgsConstructor
public static class Key<T extends Number> {
public static final List<Key> keys = new ArrayList<>();
{
keys.add(this);
}
private final Function<SteamwarUser, T> valueFunction;
public T get(SteamwarUser user) {
return valueFunction.apply(user);
}
public Function<SteamwarUser, Integer> max(int neededValue) {
return user -> {
double value = values.get(user).get(Key.this).value.doubleValue();
if (value > neededValue) return Math.min(neededValue, 100);
return (int) (value / Math.max(neededValue / 100.0, 1));
};
}
public Function<SteamwarUser, Integer> reached(int neededValue) {
return user -> {
double value = values.get(user).get(Key.this).value.doubleValue();
return value >= neededValue ? 1 : 0;
};
}
}
@Getter
private T value;
public void update(SteamwarUser user, Key<T> key) {
this.value = key.valueFunction.apply(user);
}
}
@ToString
public static class Data {
private final Advancement advancement;
@@ -42,6 +42,65 @@ public class Advancements {
@Getter
private static final List<Advancement> playtime = new ArrayList<>();
public static final Advancement.Value.Key<Double> PLAY_TIME_KEY = new Advancement.Value.Key<>(user -> {
double playtime = user.getOnlinetime();
playtime += Instant.now().getEpochSecond() - Storage.sessions.get(Chatter.of(user).getPlayer()).toInstant().getEpochSecond();
playtime /= 60d * 60d;
return playtime;
});
public static final Advancement.Value.Key<Long> FIGHT_COUNT = new Advancement.Value.Key<>(user -> {
return FightPlayer.countFights(user.getId());
});
public static final Advancement.Value.Key<Integer> FIGHT_COUNT_WAR_GEAR = new Advancement.Value.Key<>(user -> {
return FightPlayer.countFights(user.getId(), "WarGear");
});
public static final Advancement.Value.Key<Integer> FIGHT_COUNT_MINI_WAR_GEAR = new Advancement.Value.Key<>(user -> {
return FightPlayer.countFights(user.getId(), "MiniWarGear");
});
public static final Advancement.Value.Key<Integer> FIGHT_COUNT_WAR_SHIP = new Advancement.Value.Key<>(user -> {
return FightPlayer.countFights(user.getId(), "WarShip");
});
public static final Advancement.Value.Key<Long> EVENT_FIGHT_COUNT = new Advancement.Value.Key<>(user -> {
return EventFight.countEventFights(user);
});
public static final Advancement.Value.Key<Integer> EVENT_FIGHT_FIRST_PLACE_COUNT = new Advancement.Value.Key<>(user -> {
return EventFight.countPlacement(user, 1);
});
public static final Advancement.Value.Key<Integer> EVENT_FIGHT_SECOND_PLACE_COUNT = new Advancement.Value.Key<>(user -> {
return EventFight.countPlacement(user, 2);
});
public static final Advancement.Value.Key<Integer> EVENT_FIGHT_THIRDPLACE_COUNT = new Advancement.Value.Key<>(user -> {
return EventFight.countPlacement(user, 3);
});
public static final Advancement.Value.Key<Long> CHECKED_SCHEMATIC_COUNT = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countChecked(user);
});
public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countAccepted(user);
});
public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_WAR_GEAR = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countAccepted(user, "WarGear");
});
public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_MINI_WAR_GEAR = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countAccepted(user, "MiniWarGear");
});
public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_WAR_SHIP = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countAccepted(user, "WarShip");
});
public static final Advancement ROOT = new Advancement(
"steamwar:advancements/root",
Optional.empty(),
@@ -77,14 +136,7 @@ public class Advancements {
),
Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(neededPlayTime, 100),
user -> {
double playtime = user.getOnlinetime();
playtime += Instant.now().getEpochSecond() - Storage.sessions.get(Chatter.of(user).getPlayer()).toInstant().getEpochSecond();
playtime /= 60d * 60d;
if (playtime > neededPlayTime) return Math.min(neededPlayTime, 100);
return (int) (playtime / Math.max(neededPlayTime / 100.0, 1));
}
PLAY_TIME_KEY.max(neededPlayTime)
);
playtime.add(previous);
}
@@ -108,22 +160,18 @@ public class Advancements {
),
Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(fightCount, 100),
user -> {
long fights = FightPlayer.countFights(user.getId());
if (fights > fightCount) return Math.min(fightCount, 100);
return (int) (fights / Math.max(fightCount / 100.0, 1));
}
FIGHT_COUNT.max(fightCount)
);
if (i == 0) {
fightsPerType(previous, 5f, "WarGear", "stone_bricks");
fightsPerType(previous, 6f, "MiniWarGear", "stone_brick_slab");
fightsPerType(previous, 7f, "WarShip", "dark_oak_boat");
fightsPerType(previous, 5f, "WarGear", FIGHT_COUNT_WAR_GEAR, "stone_bricks");
fightsPerType(previous, 6f, "MiniWarGear", FIGHT_COUNT_MINI_WAR_GEAR, "stone_brick_slab");
fightsPerType(previous, 7f, "WarShip", FIGHT_COUNT_WAR_SHIP, "dark_oak_boat");
}
}
}
private static void fightsPerType(Advancement previous, float yCoord, String type, String item) {
private static void fightsPerType(Advancement previous, float yCoord, String type, Advancement.Value.Key<Integer> typeKey, String item) {
int[] fightCounts = new int[]{1, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000};
for (int i = 0; i < fightCounts.length; i++) {
int fightCount = fightCounts[i];
@@ -140,11 +188,7 @@ public class Advancements {
),
i == 0 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(fightCount, 100),
user -> {
long fights = FightPlayer.countFights(user.getId(), type);
if (fights > fightCount) return Math.min(fightCount, 100);
return (int) (fights / Math.max(fightCount / 100.0, 1));
}
typeKey.max(fightCount)
);
}
}
@@ -167,22 +211,18 @@ public class Advancements {
),
Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(eventFightCount, 100),
user -> {
long fights = EventFight.countEventFights(user);
if (fights > eventFightCount) return Math.min(eventFightCount, 100);
return (int) (fights / Math.max(eventFightCount / 100.0, 1));
}
EVENT_FIGHT_COUNT.max(eventFightCount)
);
if (i == 0) {
placementsCounts(previous, 9f, 1, "gold_block", Advancement.Display.FrameType.CHALLENGE);
placementsCounts(previous, 10f, 2, "iron_block", Advancement.Display.FrameType.GOAL);
placementsCounts(previous, 11f, 3, "copper_block", Advancement.Display.FrameType.TASK);
placementsCounts(previous, 9f, 1, "gold_block", EVENT_FIGHT_FIRST_PLACE_COUNT, Advancement.Display.FrameType.CHALLENGE);
placementsCounts(previous, 10f, 2, "iron_block", EVENT_FIGHT_SECOND_PLACE_COUNT, Advancement.Display.FrameType.GOAL);
placementsCounts(previous, 11f, 3, "copper_block", EVENT_FIGHT_THIRDPLACE_COUNT, Advancement.Display.FrameType.TASK);
}
}
}
private static void placementsCounts(Advancement previous, float yCoord, int placement, String item, Advancement.Display.FrameType frameType) {
private static void placementsCounts(Advancement previous, float yCoord, int placement, String item, Advancement.Value.Key<Integer> typeKey, Advancement.Display.FrameType frameType) {
for (int placementCount = 1; placementCount <= 10; placementCount++) {
int finalPlacementCount = placementCount;
previous = new Advancement(
@@ -198,7 +238,7 @@ public class Advancements {
),
placementCount == 1 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
1,
user -> EventFight.countPlacement(user, placement) >= finalPlacementCount ? 1 : 0
typeKey.reached(placementCount)
);
}
}
@@ -221,11 +261,7 @@ public class Advancements {
),
i == 0 ? Advancement.HidePolicy.NO_PROGRESS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(checkedCount, 100),
user -> {
long checked = CheckedSchematic.countChecked(user);
if (checked > checkedCount) return Math.min(checkedCount, 100);
return (int) (checked / Math.max(checkedCount / 100.0, 1));
}
CHECKED_SCHEMATIC_COUNT.max(checkedCount)
);
}
}
@@ -248,22 +284,18 @@ public class Advancements {
),
Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(acceptedCount, 100),
user -> {
long accepted = CheckedSchematic.countAccepted(user);
if (accepted > acceptedCount) return Math.min(acceptedCount, 100);
return (int) (accepted / Math.max(acceptedCount / 100.0, 1));
}
ACCEPTED_SCHEMATIC_COUNT.max(acceptedCount)
);
if (i == 0) {
acceptedPerType(previous, 2f, "WarGear", "end_stone_bricks");
acceptedPerType(previous, 3f, "MiniWarGear", "end_stone_brick_slab");
acceptedPerType(previous, 4f, "WarShip", "oak_boat");
acceptedPerType(previous, 2f, "WarGear", ACCEPTED_SCHEMATIC_COUNT_WAR_GEAR, "end_stone_bricks");
acceptedPerType(previous, 3f, "MiniWarGear", ACCEPTED_SCHEMATIC_COUNT_MINI_WAR_GEAR, "end_stone_brick_slab");
acceptedPerType(previous, 4f, "WarShip", ACCEPTED_SCHEMATIC_COUNT_WAR_SHIP, "oak_boat");
}
}
}
private static void acceptedPerType(Advancement previous, float xCoord, String type, String item) {
private static void acceptedPerType(Advancement previous, float xCoord, String type, Advancement.Value.Key<Long> typeKey, String item) {
new Advancement(
"steamwar:advancements/accepted_" + type,
Optional.of(previous),
@@ -277,7 +309,7 @@ public class Advancements {
),
Advancement.HidePolicy.WITH_PREVIOUS,
1,
user -> CheckedSchematic.countAccepted(user, type) > 0 ? 1 : 0
typeKey.reached(1)
);
}
}
@@ -35,6 +35,7 @@ import de.steamwar.velocitycore.listeners.BasicListener;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@@ -46,16 +47,11 @@ public class AdvancementsManager extends BasicListener {
static {
selectAdvancementTabPacket = new SelectAdvancementTabPacket(Optional.of("steamwar:advancements/root"));
try {
StateRegistry.PacketRegistry.ProtocolRegistry registry = StateRegistry.PLAY.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND, ProtocolVersion.MINECRAFT_1_21_6);
Field field = StateRegistry.PacketRegistry.ProtocolRegistry.class.getDeclaredField("packetClassToId");
field.setAccessible(true);
Object2IntMap<Class<? extends MinecraftPacket>> map = (Object2IntMap) field.get(registry);
map.put(SelectAdvancementTabPacket.class, 0x4E);
map.put(Advancement.Packet.class, 0x7B);
} catch (Exception e) {
// Ignore
}
registerPacketId(ProtocolVersion.MINECRAFT_1_21_9, 0x53, 0x80);
registerPacketId(ProtocolVersion.MINECRAFT_1_21_7, 0x4E, 0x7B);
registerPacketId(ProtocolVersion.MINECRAFT_1_21_6, 0x4E, 0x7B);
registerPacketId(ProtocolVersion.MINECRAFT_1_21_5, 0x4E, 0x7B);
registerPacketId(ProtocolVersion.MINECRAFT_1_21_4, 0x4F, 0x7B);
VelocityCore.schedule(() -> {
for (Advancement advancement : Advancements.getPlaytime()) {
@@ -66,18 +62,44 @@ public class AdvancementsManager extends BasicListener {
}).repeat(10, TimeUnit.MINUTES).schedule();
}
private static void registerPacketId(ProtocolVersion version, int selectAdvancementTabPacket, int advancementPacket) {
try {
StateRegistry.PacketRegistry.ProtocolRegistry registry = StateRegistry.PLAY.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND, version);
Field field = StateRegistry.PacketRegistry.ProtocolRegistry.class.getDeclaredField("packetClassToId");
field.setAccessible(true);
Object2IntMap<Class<? extends MinecraftPacket>> map = (Object2IntMap) field.get(registry);
map.put(SelectAdvancementTabPacket.class, selectAdvancementTabPacket);
map.put(Advancement.Packet.class, advancementPacket);
} catch (Exception e) {
// Ignore
}
}
@Subscribe(priority = -1000)
public void onPostLogin(PostLoginEvent event) {
// Only enable for 1.21.4 or higher
if (event.getPlayer().getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
return;
}
((ConnectedPlayer) event.getPlayer()).getConnection().write(selectAdvancementTabPacket);
SteamwarUser user = SteamwarUser.get(event.getPlayer().getUniqueId());
Advancement.Value.Key.keys.forEach(key -> {
Advancement.Value value = new Advancement.Value();
value.update(user, key);
Advancement.values.computeIfAbsent(user, k -> new HashMap<>()).put(key, value);
});
for (Advancement advancement : Advancements.getAll()) {
advancement.get(SteamwarUser.get(event.getPlayer().getUniqueId()));
advancement.get(user);
}
}
@Subscribe
public void onDisconnect(DisconnectEvent event) {
SteamwarUser user = SteamwarUser.get(event.getPlayer().getUniqueId());
for (Advancement advancement : Advancements.getAll()) {
advancement.data.remove(SteamwarUser.get(event.getPlayer().getUniqueId()));
advancement.data.remove(user);
}
Advancement.values.remove(user);
}
}