forked from SteamWar/SteamWar
Merge pull request 'Velocity core/ advancements' (#429) from VelocityCore/Advancements into main
Reviewed-on: SteamWar/SteamWar#429
This commit is contained in:
@@ -86,6 +86,24 @@ class CheckedSchematic(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
|||||||
useDb {
|
useDb {
|
||||||
find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.seen eq false) }.orderBy(CheckedSchematicTable.endTime to SortOrder.DESC).toList()
|
find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.seen eq false) }.orderBy(CheckedSchematicTable.endTime to SortOrder.DESC).toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun countAccepted(owner: SteamwarUser) =
|
||||||
|
useDb {
|
||||||
|
find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.declineReason eq "freigegeben") }.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun countAccepted(owner: SteamwarUser, type: String) =
|
||||||
|
useDb {
|
||||||
|
find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.declineReason eq "freigegeben") and (CheckedSchematicTable.nodeType like "$type%") }.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun countChecked(validator: SteamwarUser) =
|
||||||
|
useDb {
|
||||||
|
find { CheckedSchematicTable.validator eq validator.id }.count()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val node by CheckedSchematicTable.nodeId.transform({ it?.let { EntityID(it, SchematicNodeTable) } }, { it?.value })
|
val node by CheckedSchematicTable.nodeId.transform({ it?.let { EntityID(it, SchematicNodeTable) } }, { it?.value })
|
||||||
|
|||||||
@@ -130,6 +130,43 @@ class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun countEventFights(fighter: SteamwarUser) =
|
||||||
|
useDb {
|
||||||
|
exec(
|
||||||
|
"SELECT COUNT(DISTINCT F.FightID) AS FightCount FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID INNER JOIN EventFight EF on F.FightID = EF.Fight WHERE UserID = ?",
|
||||||
|
args = listOf(IntegerColumnType() to fighter.id.value)
|
||||||
|
) {
|
||||||
|
if (it.next()) {
|
||||||
|
it.getLong("FightCount")
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun countPlacement(fighter: SteamwarUser, placement: Int) =
|
||||||
|
useDb {
|
||||||
|
exec(
|
||||||
|
"""
|
||||||
|
SELECT COUNT(DISTINCT EventFight.EventID) AS PlacementCount FROM TeamTeilnahme
|
||||||
|
INNER JOIN EventFight ON EventFight.EventID = TeamTeilnahme.EventID
|
||||||
|
INNER JOIN FightPlayer ON FightPlayer.FightID = EventFight.Fight
|
||||||
|
WHERE (IF(FightPlayer.Team = 1, EventFight.TeamBlue, EventFight.TeamRed)) = TeamTeilnahme.TeamID AND UserID = ? AND Placement = ?
|
||||||
|
""".trimIndent(),
|
||||||
|
args = listOf(IntegerColumnType() to fighter.id.value, IntegerColumnType() to placement)
|
||||||
|
) {
|
||||||
|
if (it.next()) {
|
||||||
|
it.getInt("PlacementCount")
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val fightID by EventFightTable.id.transform({ EntityID(it, EventFightTable) }, { it.value })
|
val fightID by EventFightTable.id.transform({ EntityID(it, EventFightTable) }, { it.value })
|
||||||
|
|||||||
@@ -20,9 +20,12 @@
|
|||||||
package de.steamwar.sql
|
package de.steamwar.sql
|
||||||
|
|
||||||
import de.steamwar.sql.internal.useDb
|
import de.steamwar.sql.internal.useDb
|
||||||
|
import org.jetbrains.exposed.v1.core.IntegerColumnType
|
||||||
|
import org.jetbrains.exposed.v1.core.VarCharColumnType
|
||||||
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
|
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
|
||||||
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
|
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
|
||||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
import org.jetbrains.exposed.v1.core.inList
|
import org.jetbrains.exposed.v1.core.inList
|
||||||
import org.jetbrains.exposed.v1.dao.CompositeEntity
|
import org.jetbrains.exposed.v1.dao.CompositeEntity
|
||||||
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
|
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
|
||||||
@@ -69,6 +72,28 @@ class FightPlayer(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
|||||||
useDb {
|
useDb {
|
||||||
find { FightPlayerTable.fightId inList fightIds.toList() }.toList()
|
find { FightPlayerTable.fightId inList fightIds.toList() }.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun countFights(userId: Int) =
|
||||||
|
useDb {
|
||||||
|
find { FightPlayerTable.userId eq userId }.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun countFights(userId: Int, type: String) =
|
||||||
|
useDb {
|
||||||
|
exec(
|
||||||
|
"SELECT COUNT(*) AS FightCount FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID WHERE UserID = ? AND GameMode LIKE ?",
|
||||||
|
args = listOf(IntegerColumnType() to userId, VarCharColumnType() to "$type%")
|
||||||
|
) {
|
||||||
|
if (it.next()) {
|
||||||
|
it.getInt("FightCount")
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val fightID by FightPlayerTable.fightId.transform({ EntityID(it, FightTable) }, { it.value })
|
val fightID by FightPlayerTable.fightId.transform({ EntityID(it, FightTable) }, { it.value })
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.velocitycore.advancements;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
public class Items {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loaded from https://github.com/retrooper/packetevents/blob/2.0/mappings/registries/item.json
|
||||||
|
*/
|
||||||
|
public static final JsonObject values;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
values = new Gson().fromJson(new BufferedReader(new InputStreamReader(URI.create("https://raw.githubusercontent.com/retrooper/packetevents/refs/heads/2.0/mappings/registries/item.json").toURL().openConnection().getInputStream())), JsonObject.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,340 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.velocitycore.advancements;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
|
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.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.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Advancement.Data get(SteamwarUser user, BiFunction<Advancement, SteamwarUser, Data> function) {
|
||||||
|
if (data.containsKey(user)) return data.get(user);
|
||||||
|
return function.apply(this, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String identifier;
|
||||||
|
private final Optional<Advancement> parent;
|
||||||
|
private final Display display;
|
||||||
|
|
||||||
|
private final HidePolicy hidePolicy;
|
||||||
|
private final int total;
|
||||||
|
|
||||||
|
private final Function<SteamwarUser, Integer> progressCalculator;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder st = new StringBuilder();
|
||||||
|
st.append("Advancement(");
|
||||||
|
parent.ifPresent(advancement -> st.append(advancement.identifier).append("<-"));
|
||||||
|
st.append(identifier);
|
||||||
|
st.append(", total=").append(total);
|
||||||
|
st.append(")");
|
||||||
|
return st.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class Display {
|
||||||
|
private final Component title;
|
||||||
|
private final Component description;
|
||||||
|
private final String item;
|
||||||
|
private final FrameType frameType;
|
||||||
|
private Optional<String> background = Optional.empty();
|
||||||
|
private final float xCoord;
|
||||||
|
private final float yCoord;
|
||||||
|
|
||||||
|
public enum FrameType {
|
||||||
|
TASK,
|
||||||
|
CHALLENGE,
|
||||||
|
GOAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum HidePolicy {
|
||||||
|
NEVER {
|
||||||
|
@Override
|
||||||
|
public boolean hidden(Data data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
NO_PROGRESS {
|
||||||
|
@Override
|
||||||
|
public boolean hidden(Data data) {
|
||||||
|
return data.progress == 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PREVIOUS_UNFINISHED {
|
||||||
|
@Override
|
||||||
|
public boolean hidden(Data data) {
|
||||||
|
if (data.advancement.parent.isPresent()) {
|
||||||
|
Advancement parent = data.advancement.parent.get();
|
||||||
|
Advancement.Data parentData = parent.get(data.user);
|
||||||
|
return parentData.progress != parentData.advancement.total;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
WITH_PREVIOUS {
|
||||||
|
@Override
|
||||||
|
public boolean hidden(Data data) {
|
||||||
|
if (data.advancement.parent.isPresent()) {
|
||||||
|
Advancement parent = data.advancement.parent.get();
|
||||||
|
Advancement.Data parentData = parent.get(data.user);
|
||||||
|
return parentData.hidden;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
private Advancement.Value get(SteamwarUser user) {
|
||||||
|
Key self = this;
|
||||||
|
return values.computeIfAbsent(user, __ -> new HashMap<>()).computeIfAbsent(self, __ -> {
|
||||||
|
Value data = new Advancement.Value();
|
||||||
|
data.update(user, self);
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function<SteamwarUser, Integer> max(int neededValue) {
|
||||||
|
return user -> {
|
||||||
|
double value = get(user).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 = get(user).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;
|
||||||
|
private final SteamwarUser user;
|
||||||
|
|
||||||
|
private int progress;
|
||||||
|
private boolean showToast = true;
|
||||||
|
private boolean hidden = false;
|
||||||
|
|
||||||
|
public Data(Advancement advancement, SteamwarUser user) {
|
||||||
|
this.advancement = advancement;
|
||||||
|
advancement.data.put(user, this);
|
||||||
|
this.user = user;
|
||||||
|
this.progress = advancement.progressCalculator.apply(user);
|
||||||
|
checkHidden();
|
||||||
|
checkFinished();
|
||||||
|
new Packet(this, showToast).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Data(Advancement advancement, SteamwarUser user, int progress) {
|
||||||
|
this.advancement = advancement;
|
||||||
|
advancement.data.put(user, this);
|
||||||
|
this.user = user;
|
||||||
|
this.progress = progress;
|
||||||
|
checkHidden();
|
||||||
|
checkFinished();
|
||||||
|
new Packet(this, showToast).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
this.progress = advancement.progressCalculator.apply(user);
|
||||||
|
checkHidden();
|
||||||
|
|
||||||
|
new Packet(this, showToast).send();
|
||||||
|
// Update Advancements that have this as parent
|
||||||
|
Advancements.getAll()
|
||||||
|
.stream()
|
||||||
|
.filter(advancement -> advancement.parent.filter(value -> value == this.advancement).isPresent())
|
||||||
|
.map(advancement -> advancement.get(user))
|
||||||
|
.forEach(Advancement.Data::update);
|
||||||
|
|
||||||
|
checkFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkHidden() {
|
||||||
|
hidden = advancement.hidePolicy.hidden(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkFinished() {
|
||||||
|
if (progress == advancement.total) {
|
||||||
|
showToast = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void encodeAdvancement(ByteBuf byteBuf, ProtocolVersion protocolVersion, boolean showToast) {
|
||||||
|
ProtocolUtils.writeString(byteBuf, advancement.identifier);
|
||||||
|
if (advancement.parent.isPresent()) {
|
||||||
|
byteBuf.writeBoolean(true);
|
||||||
|
ProtocolUtils.writeString(byteBuf, advancement.parent.get().identifier);
|
||||||
|
} else {
|
||||||
|
byteBuf.writeBoolean(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Display
|
||||||
|
byteBuf.writeBoolean(true);
|
||||||
|
new ComponentHolder(protocolVersion, advancement.display.title).write(byteBuf);
|
||||||
|
new ComponentHolder(protocolVersion, advancement.display.description).write(byteBuf);
|
||||||
|
{ // Slot
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 1);
|
||||||
|
int itemId = Items.values
|
||||||
|
.get(protocolVersion.name().replace("MINECRAFT_", "V_"))
|
||||||
|
.getAsJsonObject()
|
||||||
|
.get(advancement.display.item)
|
||||||
|
.getAsInt();
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, itemId);
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 0);
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 0);
|
||||||
|
}
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, advancement.display.frameType.ordinal());
|
||||||
|
if (advancement.display.background.isPresent()) {
|
||||||
|
byteBuf.writeInt(0x01 | (showToast ? 0x02 : 0x00) | (hidden ? 0x04 : 0x00));
|
||||||
|
ProtocolUtils.writeString(byteBuf, advancement.display.background.get());
|
||||||
|
} else {
|
||||||
|
byteBuf.writeInt((showToast ? 0x02 : 0x00) | (hidden ? 0x04 : 0x00));
|
||||||
|
}
|
||||||
|
byteBuf.writeFloat(advancement.display.xCoord);
|
||||||
|
byteBuf.writeFloat(advancement.display.yCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, advancement.total);
|
||||||
|
for (int i = 0; i < advancement.total; i++) {
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 1);
|
||||||
|
ProtocolUtils.writeString(byteBuf, advancement.identifier + "_" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
byteBuf.writeBoolean(false); // No Telemetry
|
||||||
|
}
|
||||||
|
|
||||||
|
private void encodeProgress(ByteBuf byteBuf) {
|
||||||
|
ProtocolUtils.writeString(byteBuf, this.advancement.identifier);
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, advancement.total);
|
||||||
|
for (int i = 0; i < advancement.total; i++) {
|
||||||
|
ProtocolUtils.writeString(byteBuf, advancement.identifier + "_" + i);
|
||||||
|
if (i == advancement.total - 1 && advancement.total == progress) {
|
||||||
|
byteBuf.writeBoolean(true);
|
||||||
|
byteBuf.writeLong(new Date().getTime());
|
||||||
|
} else if (i < progress) {
|
||||||
|
byteBuf.writeBoolean(true);
|
||||||
|
byteBuf.writeLong(0);
|
||||||
|
} else {
|
||||||
|
byteBuf.writeBoolean(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected record Packet(Data data, boolean showToast) implements MinecraftPacket {
|
||||||
|
public void send() {
|
||||||
|
Player player = Chatter.of(data.user).getPlayer();
|
||||||
|
((ConnectedPlayer) player).getConnection().write(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||||
|
byteBuf.writeBoolean(false); // Clear
|
||||||
|
|
||||||
|
if (!data.hidden) {
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 1);
|
||||||
|
data.encodeAdvancement(byteBuf, protocolVersion, showToast);
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 0); // No Advancements to remove
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 1);
|
||||||
|
data.encodeProgress(byteBuf);
|
||||||
|
} else {
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 0); // No Advancements to update
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 1);
|
||||||
|
ProtocolUtils.writeString(byteBuf, data.advancement.identifier);
|
||||||
|
ProtocolUtils.writeVarInt(byteBuf, 0); // No Advancements Progress to update
|
||||||
|
}
|
||||||
|
|
||||||
|
byteBuf.writeBoolean(true); // Show Advancements
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handle(MinecraftSessionHandler minecraftSessionHandler) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.velocitycore.advancements;
|
||||||
|
|
||||||
|
import de.steamwar.messages.Chatter;
|
||||||
|
import de.steamwar.persistent.Storage;
|
||||||
|
import de.steamwar.sql.CheckedSchematic;
|
||||||
|
import de.steamwar.sql.EventFight;
|
||||||
|
import de.steamwar.sql.FightPlayer;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class Advancements {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
static final List<Advancement> all = new ArrayList<>();
|
||||||
|
|
||||||
|
@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(),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text("SteamWar"),
|
||||||
|
Component.text("Join SteamWar for the first time!"),
|
||||||
|
"cactus_flower",
|
||||||
|
Advancement.Display.FrameType.CHALLENGE,
|
||||||
|
Optional.of("minecraft:gui/advancements/backgrounds/adventure"),
|
||||||
|
0f,
|
||||||
|
3f
|
||||||
|
),
|
||||||
|
Advancement.HidePolicy.NEVER,
|
||||||
|
1,
|
||||||
|
user -> 1
|
||||||
|
);
|
||||||
|
|
||||||
|
static {
|
||||||
|
Advancement previous = ROOT;
|
||||||
|
int[] playTimes = new int[]{1, 10, 100, 500, 1000, 2500, 5000, 7500, 10000, 15000, 20000};
|
||||||
|
for (int i = 0; i < playTimes.length; i++) {
|
||||||
|
int neededPlayTime = playTimes[i];
|
||||||
|
previous = new Advancement(
|
||||||
|
"steamwar:advancements/playtime_" + neededPlayTime + "_hour",
|
||||||
|
Optional.of(previous),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text("Play " + neededPlayTime + " Hour" + (neededPlayTime > 1 ? "s" : "")),
|
||||||
|
Component.text("Play " + neededPlayTime + " hour" + (neededPlayTime > 1 ? "s" : "") + " on SteamWar"),
|
||||||
|
"clock",
|
||||||
|
Advancement.Display.FrameType.TASK,
|
||||||
|
i + 1f,
|
||||||
|
3f
|
||||||
|
),
|
||||||
|
Advancement.HidePolicy.PREVIOUS_UNFINISHED,
|
||||||
|
Math.min(neededPlayTime, 100),
|
||||||
|
PLAY_TIME_KEY.max(neededPlayTime)
|
||||||
|
);
|
||||||
|
playtime.add(previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
Advancement previous = ROOT;
|
||||||
|
int[] fightCounts = new int[]{1, 10, 50, 100, 200, 500, 1000, 2500, 5000, 7500, 10000, 15000, 20000};
|
||||||
|
for (int i = 0; i < fightCounts.length; i++) {
|
||||||
|
int fightCount = fightCounts[i];
|
||||||
|
previous = new Advancement(
|
||||||
|
"steamwar:advancements/fights_" + fightCount,
|
||||||
|
Optional.of(previous),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text(fightCount + " Fight" + (fightCount > 1 ? "s" : "")),
|
||||||
|
Component.text(fightCount + " Fight" + (fightCount > 1 ? "s" : "")),
|
||||||
|
"iron_sword",
|
||||||
|
Advancement.Display.FrameType.TASK,
|
||||||
|
i + 1f,
|
||||||
|
4f
|
||||||
|
),
|
||||||
|
Advancement.HidePolicy.PREVIOUS_UNFINISHED,
|
||||||
|
Math.min(fightCount, 100),
|
||||||
|
FIGHT_COUNT.max(fightCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
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, 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];
|
||||||
|
previous = new Advancement(
|
||||||
|
"steamwar:advancements/fights_" + type + "_" + fightCount,
|
||||||
|
Optional.of(previous),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text(type + " " + fightCount + " Fight" + (fightCount > 1 ? "s" : "")),
|
||||||
|
Component.text(type + " " + fightCount + " Fight" + (fightCount > 1 ? "s" : "")),
|
||||||
|
item,
|
||||||
|
Advancement.Display.FrameType.TASK,
|
||||||
|
i + 2f,
|
||||||
|
yCoord
|
||||||
|
),
|
||||||
|
i == 0 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
|
||||||
|
Math.min(fightCount, 100),
|
||||||
|
typeKey.max(fightCount)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
Advancement previous = ROOT;
|
||||||
|
int[] eventFightCounts = new int[]{1, 5, 10, 15, 25, 50, 100, 150, 200, 250};
|
||||||
|
for (int i = 0; i < eventFightCounts.length; i++) {
|
||||||
|
int eventFightCount = eventFightCounts[i];
|
||||||
|
previous = new Advancement(
|
||||||
|
"steamwar:advancements/event_fights_" + eventFightCount,
|
||||||
|
Optional.of(previous),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text(eventFightCount + " Event-Fight" + (eventFightCount > 1 ? "s" : "")),
|
||||||
|
Component.text(eventFightCount + " Event-Fight" + (eventFightCount > 1 ? "s" : "")),
|
||||||
|
"golden_sword",
|
||||||
|
Advancement.Display.FrameType.TASK,
|
||||||
|
i + 1f,
|
||||||
|
8f
|
||||||
|
),
|
||||||
|
Advancement.HidePolicy.PREVIOUS_UNFINISHED,
|
||||||
|
Math.min(eventFightCount, 100),
|
||||||
|
EVENT_FIGHT_COUNT.max(eventFightCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
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.Value.Key<Integer> typeKey, Advancement.Display.FrameType frameType) {
|
||||||
|
for (int placementCount = 1; placementCount <= 10; placementCount++) {
|
||||||
|
int finalPlacementCount = placementCount;
|
||||||
|
previous = new Advancement(
|
||||||
|
"steamwar:advancements/event_placement_" + placement + "_" + placementCount,
|
||||||
|
Optional.of(previous),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text(placementCount + "x " + placement + ". Place in Event"),
|
||||||
|
Component.text(""),
|
||||||
|
item,
|
||||||
|
frameType,
|
||||||
|
2f + (placementCount - 1f),
|
||||||
|
yCoord
|
||||||
|
),
|
||||||
|
placementCount == 1 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
|
||||||
|
1,
|
||||||
|
typeKey.reached(placementCount)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
Advancement previous = ROOT;
|
||||||
|
int[] checkedCounts = new int[]{1, 10, 100, 250, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000};
|
||||||
|
for (int i = 0; i < checkedCounts.length; i++) {
|
||||||
|
int checkedCount = checkedCounts[i];
|
||||||
|
previous = new Advancement(
|
||||||
|
"steamwar:advancements/checked_" + checkedCount,
|
||||||
|
Optional.of(previous),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text(checkedCount + " Check Session" + (checkedCount > 1 ? "s" : "")),
|
||||||
|
Component.text(checkedCount + " Check Session" + (checkedCount > 1 ? "s" : "")),
|
||||||
|
"paper",
|
||||||
|
Advancement.Display.FrameType.TASK,
|
||||||
|
i + 1f,
|
||||||
|
0f
|
||||||
|
),
|
||||||
|
i == 0 ? Advancement.HidePolicy.NO_PROGRESS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
|
||||||
|
Math.min(checkedCount, 100),
|
||||||
|
CHECKED_SCHEMATIC_COUNT.max(checkedCount)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
Advancement previous = ROOT;
|
||||||
|
int[] acceptedCounts = new int[]{1, 5, 10, 15, 25, 50, 100, 150, 200, 250, 500, 750, 1000};
|
||||||
|
for (int i = 0; i < acceptedCounts.length; i++) {
|
||||||
|
int acceptedCount = acceptedCounts[i];
|
||||||
|
previous = new Advancement(
|
||||||
|
"steamwar:advancements/accepted_" + acceptedCount,
|
||||||
|
Optional.of(previous),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text(acceptedCount + " Accepted Schematic" + (acceptedCount > 1 ? "s" : "")),
|
||||||
|
Component.text(acceptedCount + " Accepted Schematic" + (acceptedCount > 1 ? "s" : "")),
|
||||||
|
"cauldron",
|
||||||
|
Advancement.Display.FrameType.TASK,
|
||||||
|
i + 1f,
|
||||||
|
2f
|
||||||
|
),
|
||||||
|
Advancement.HidePolicy.PREVIOUS_UNFINISHED,
|
||||||
|
Math.min(acceptedCount, 100),
|
||||||
|
ACCEPTED_SCHEMATIC_COUNT.max(acceptedCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
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, Advancement.Value.Key<Long> typeKey, String item) {
|
||||||
|
new Advancement(
|
||||||
|
"steamwar:advancements/accepted_" + type,
|
||||||
|
Optional.of(previous),
|
||||||
|
new Advancement.Display(
|
||||||
|
Component.text(type + " Accepted"),
|
||||||
|
Component.text(""),
|
||||||
|
item,
|
||||||
|
Advancement.Display.FrameType.GOAL,
|
||||||
|
xCoord,
|
||||||
|
1f
|
||||||
|
),
|
||||||
|
Advancement.HidePolicy.WITH_PREVIOUS,
|
||||||
|
1,
|
||||||
|
typeKey.reached(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.velocitycore.advancements;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.event.Subscribe;
|
||||||
|
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||||
|
import com.velocitypowered.api.event.connection.PostLoginEvent;
|
||||||
|
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
||||||
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
|
import de.steamwar.linkage.Linked;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import de.steamwar.velocitycore.listeners.BasicListener;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Linked
|
||||||
|
public class AdvancementsManager extends BasicListener {
|
||||||
|
|
||||||
|
private static SelectAdvancementTabPacket selectAdvancementTabPacket;
|
||||||
|
|
||||||
|
static {
|
||||||
|
selectAdvancementTabPacket = new SelectAdvancementTabPacket(Optional.of("steamwar:advancements/root"));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
sendAdvancements(event.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(priority = -1000)
|
||||||
|
public void onServerPostConnect(ServerPostConnectEvent event) {
|
||||||
|
sendAdvancements(event.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendAdvancements(Player player) {
|
||||||
|
// Only enable for 1.21.4 or higher
|
||||||
|
if (player.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
((ConnectedPlayer) player).getConnection().write(selectAdvancementTabPacket);
|
||||||
|
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
|
||||||
|
for (Advancement advancement : Advancements.getAll()) {
|
||||||
|
advancement.get(user).update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onDisconnect(DisconnectEvent event) {
|
||||||
|
SteamwarUser user = SteamwarUser.get(event.getPlayer().getUniqueId());
|
||||||
|
for (Advancement advancement : Advancements.getAll()) {
|
||||||
|
advancement.data.remove(user);
|
||||||
|
}
|
||||||
|
Advancement.values.remove(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.velocitycore.advancements;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SelectAdvancementTabPacket implements MinecraftPacket {
|
||||||
|
|
||||||
|
private Optional<String> identifier;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||||
|
throw new UnsupportedOperationException("Packet is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||||
|
if (this.identifier.isPresent()) {
|
||||||
|
byteBuf.writeBoolean(true);
|
||||||
|
ProtocolUtils.writeString(byteBuf, this.identifier.get());
|
||||||
|
} else {
|
||||||
|
byteBuf.writeBoolean(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handle(MinecraftSessionHandler minecraftSessionHandler) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,6 +35,9 @@ import de.steamwar.sql.CheckedSchematic;
|
|||||||
import de.steamwar.sql.SchematicType;
|
import de.steamwar.sql.SchematicType;
|
||||||
import de.steamwar.sql.SteamwarUser;
|
import de.steamwar.sql.SteamwarUser;
|
||||||
import de.steamwar.sql.UserPerm;
|
import de.steamwar.sql.UserPerm;
|
||||||
|
import de.steamwar.velocitycore.VelocityCore;
|
||||||
|
import de.steamwar.velocitycore.advancements.Advancement;
|
||||||
|
import de.steamwar.velocitycore.advancements.Advancements;
|
||||||
import de.steamwar.velocitycore.commands.*;
|
import de.steamwar.velocitycore.commands.*;
|
||||||
import de.steamwar.velocitycore.discord.DiscordBot;
|
import de.steamwar.velocitycore.discord.DiscordBot;
|
||||||
import de.steamwar.velocitycore.discord.util.DiscordRanks;
|
import de.steamwar.velocitycore.discord.util.DiscordRanks;
|
||||||
@@ -45,6 +48,7 @@ import net.kyori.adventure.text.event.ClickEvent;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Linked
|
@Linked
|
||||||
public class ConnectionListener extends BasicListener {
|
public class ConnectionListener extends BasicListener {
|
||||||
@@ -102,8 +106,12 @@ public class ConnectionListener extends BasicListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newPlayers.contains(player.getUniqueId())) {
|
if (newPlayers.contains(player.getUniqueId())) {
|
||||||
|
Advancements.ROOT.get(user, (advancement, __) -> new Advancement.Data(advancement, user, 0));
|
||||||
Chatter.broadcast().system("JOIN_FIRST", player);
|
Chatter.broadcast().system("JOIN_FIRST", player);
|
||||||
newPlayers.remove(player.getUniqueId());
|
newPlayers.remove(player.getUniqueId());
|
||||||
|
VelocityCore.schedule(() -> {
|
||||||
|
Advancements.ROOT.get(user).update();
|
||||||
|
}).delay(1, TimeUnit.SECONDS).schedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StreamingCommand.isNotStreaming(user)) {
|
if (!StreamingCommand.isNotStreaming(user)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user