Merge branch 'main' into dc-5

This commit is contained in:
2024-08-22 11:10:41 +02:00
62 changed files with 2103 additions and 279 deletions
+4
View File
@@ -11,5 +11,9 @@ steamwar.properties
.idea
*.iml
# VSCode
bin/
.vscode
# Other
lib
@@ -38,6 +38,7 @@ dependencies {
compileOnly(libs.spigotapi)
compileOnly(libs.axiom)
compileOnly(libs.authlib)
compileOnly(libs.viaapi)
compileOnly(libs.nms20)
compileOnly(libs.fawe18)
@@ -1012,3 +1012,5 @@ COLORREPLACE_HELP=§8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §
TYPEREPLACE_HELP=§8//§etypereplace §8[§7type§8] §8[§7type§8] §8- §7Replace all blocks of one type with another
# Schematic
SCHEMATIC_GUI_ITEM=§eSchematics
#VersionAnnouncer
SERVER_VERSION=§7This server runs on Minecraft version §e{0}
@@ -953,3 +953,5 @@ COLORREPLACE_HELP=§8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §
TYPEREPLACE_HELP=§8//§etyreplace §8[§7type§8] §8[§7type§8] §8- §7Ersetzt einen Blockgruppe mit einer anderen
# Schematics
SCHEMATIC_GUI_ITEM=§eSchematics
#VersionAnnouncer
SERVER_VERSION=§7Dieser Server läuft auf Minecraft-Version §e{0}
@@ -28,6 +28,8 @@ import de.steamwar.bausystem.features.script.lua.libs.LuaLib;
import de.steamwar.bausystem.features.slaves.panzern.Panzern;
import de.steamwar.bausystem.features.slaves.panzern.PanzernAlgorithm;
import de.steamwar.bausystem.features.tpslimit.TPSFreezeUtils;
import de.steamwar.bausystem.features.tracer.TraceManager;
import de.steamwar.bausystem.features.tracer.TraceRecorder;
import de.steamwar.bausystem.features.world.BauScoreboard;
import de.steamwar.bausystem.linkage.specific.BauGuiItem;
import de.steamwar.bausystem.region.loader.PrototypeLoader;
@@ -70,7 +72,7 @@ import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class BauSystem extends JavaPlugin implements Listener {
public class BauSystem extends JavaPlugin {
// This should be treated as final!
public static Message MESSAGE;
@@ -197,6 +199,9 @@ public class BauSystem extends JavaPlugin implements Listener {
});
TickListener.impl.init();
TraceManager.instance.init();
TraceRecorder.instance.init();
}
@Override
@@ -61,6 +61,9 @@ public class ResetCommand extends SWCommand {
PasteBuilder pasteBuilder = new PasteBuilder(new PasteBuilder.FileProvider(region.getResetFile(RegionType.NORMAL)))
.color(region.getPlain(Flag.COLOR, ColorMode.class).getColor());
region.reset(pasteBuilder, RegionType.NORMAL, RegionExtensionType.NORMAL);
for (Flag value : Flag.values()) {
region.set(value, value.getDefaultValue());
}
RegionUtils.message(region, "REGION_RESET_RESETED");
} catch (SecurityException e) {
BauSystem.MESSAGE.send("REGION_RESET_ERROR", p);
@@ -22,6 +22,8 @@ package de.steamwar.bausystem.features.tracer;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.utils.RegionExtensionType;
import de.steamwar.bausystem.region.utils.RegionType;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -39,8 +41,9 @@ import java.util.Optional;
/**
* Recording of a tnt at a specific tick
*/
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@Getter
public class TNTPoint implements Externalizable {
public class TNTPoint{
/**
* Unique number to identify records being of the same tnt
*/
@@ -97,12 +100,9 @@ public class TNTPoint implements Externalizable {
private List<TNTPoint> history;
/**
* Constructor for deserialization only !! Do not Call !!
* Constructor for object creation in trace recording
*/
public TNTPoint() {
}
public TNTPoint(int tntId, TNTPrimed tnt, boolean explosion, boolean afterFirstExplosion, long ticksSinceStart,
protected TNTPoint(int tntId, TNTPrimed tnt, boolean explosion, boolean afterFirstExplosion, long ticksSinceStart,
List<TNTPoint> history, List<Block> destroyedBlocks) {
this.tntId = tntId;
this.explosion = explosion;
@@ -161,44 +161,6 @@ public class TNTPoint implements Externalizable {
this.history = history;
}
@Override
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeInt(tntId);
objectOutput.writeBoolean(explosion);
objectOutput.writeBoolean(inWater);
objectOutput.writeBoolean(afterFirstExplosion);
objectOutput.writeBoolean(destroyedBuildArea);
objectOutput.writeBoolean(destroyedTestBlock);
objectOutput.writeLong(ticksSinceStart);
objectOutput.writeInt(fuse);
objectOutput.writeDouble(location.getX());
objectOutput.writeDouble(location.getY());
objectOutput.writeDouble(location.getZ());
objectOutput.writeDouble(velocity.getX());
objectOutput.writeDouble(velocity.getY());
objectOutput.writeDouble(velocity.getZ());
}
@Override
public void readExternal(ObjectInput objectInput) throws IOException {
tntId = objectInput.readInt();
explosion = objectInput.readBoolean();
inWater = objectInput.readBoolean();
afterFirstExplosion = objectInput.readBoolean();
destroyedBuildArea = objectInput.readBoolean();
destroyedTestBlock = objectInput.readBoolean();
ticksSinceStart = objectInput.readLong();
fuse = objectInput.readInt();
double locX = objectInput.readDouble();
double locY = objectInput.readDouble();
double locZ = objectInput.readDouble();
location = new Location(Bukkit.getWorlds().get(0), locX, locY, locZ);
double velX = objectInput.readDouble();
double velY = objectInput.readDouble();
double velZ = objectInput.readDouble();
velocity = new Vector(velX, velY, velZ);
}
@Override
public String toString() {
return "TNTPoint{" +
@@ -26,20 +26,16 @@ import de.steamwar.bausystem.features.tracer.rendering.ViewFlag;
import de.steamwar.bausystem.region.Region;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import lombok.Cleanup;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.io.*;
import java.io.File;
import java.lang.ref.SoftReference;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
public class Trace {
/**
@@ -77,6 +73,10 @@ public class Trace {
*/
private SoftReference<List<TNTPoint>> records;
@Setter
@Getter
private int recordsCount;
/**
* A map of all REntityServers rendering this trace
*/
@@ -91,46 +91,25 @@ public class Trace {
@SneakyThrows
public Trace(Region region, List<TNTPoint> recordList) {
this.uuid = UUID.randomUUID();
recordsSaveFile = new File(TraceManager.tracesFolder, uuid + ".records");
this.region = region;
this.date = new Date();
records = new SoftReference<>(recordList);
metadataSaveFile = new File(TraceManager.tracesFolder, uuid + ".meta");
@Cleanup
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(metadataSaveFile));
outputStream.writeUTF(uuid.toString());
outputStream.writeUTF(region.getName());
outputStream.writeObject(date);
recordsSaveFile = new File(TraceRepository.tracesFolder, uuid + ".records");
metadataSaveFile = new File(TraceRepository.tracesFolder, uuid + ".meta");
}
/**
* Constructor for serialising a trace from the file system
*
* @param metadataSaveFile the file for this traces metadata
* Constructor for deserialising a trace from the file system
*/
@SneakyThrows
public Trace(File metadataSaveFile) {
String uuid = null;
Region region = null;
Date date = null;
this.metadataSaveFile = metadataSaveFile;
try {
@Cleanup
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(metadataSaveFile));
uuid = inputStream.readUTF();
region = Region.getREGION_MAP().get(inputStream.readUTF());
date = (Date) inputStream.readObject();
inputStream.close();
} finally {
this.uuid = UUID.fromString(uuid);
this.region = region;
this.date = date;
recordsSaveFile = new File(TraceManager.tracesFolder, uuid + ".records");
this.records = new SoftReference<>(null);
}
protected Trace(UUID uuid, Region region, Date date, File metadataFile, File recordsFile, int recordsCount) {
this.metadataSaveFile = metadataFile;
recordsSaveFile = recordsFile;
this.uuid = uuid;
this.region = region;
this.date = date;
this.records = new SoftReference<>(null);
this.recordsCount = recordsCount;
}
/**
@@ -173,7 +152,8 @@ public class Trace {
entityServer = new REntityServer();
entityServer.addPlayer(player);
entityServer.setCallback((p, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.INTERACT) return;
if (entityAction != REntityServer.EntityAction.INTERACT)
return;
if (rEntity instanceof TraceEntity) {
((TraceEntity) rEntity).printIntoChat(p);
}
@@ -195,7 +175,8 @@ public class Trace {
REntityServer newEntityServer = new REntityServer();
newEntityServer.addPlayer(k);
newEntityServer.setCallback((p, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.INTERACT) return;
if (entityAction != REntityServer.EntityAction.INTERACT)
return;
if (rEntity instanceof TraceEntity) {
((TraceEntity) rEntity).printIntoChat(p);
}
@@ -234,7 +215,8 @@ public class Trace {
List<TraceEntity> entities = new LinkedList<>();
for (List<TNTPoint> bundle : bundles) {
entities.add(new TraceEntity(entityServer, bundle.get(0).getLocation(), bundle.get(0).isExplosion(), bundle, this));
entities.add(new TraceEntity(entityServer, bundle.get(0).getLocation(), bundle.get(0).isExplosion(), bundle,
this));
}
// Apply modifiers
@@ -312,38 +294,7 @@ public class Trace {
* Loads the records of this trace from storage to memory
*/
private void loadRecords() {
List<TNTPoint> records = new ArrayList<>();
long readBytes = 0;
try {
FileInputStream fileInputStream = new FileInputStream(recordsSaveFile);
@Cleanup
ObjectInputStream inputStream = new ObjectInputStream(new GZIPInputStream(fileInputStream));
long fileLenght = recordsSaveFile.length();
while (fileInputStream.getChannel().position() < fileLenght) {
records.add((TNTPoint) inputStream.readObject());
readBytes = fileInputStream.getChannel().position();
}
} catch (EOFException e) {
Logger logger = Bukkit.getLogger();
logger.log(Level.WARNING, "EOF in trace read detected in " + uuid);
logger.log(Level.WARNING, "Read " + readBytes + "/" + recordsSaveFile.length() + " Bytes");
logger.log(Level.WARNING, "Read so far: " + records);
e.printStackTrace();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
Map<Integer, List<TNTPoint>> histories = new HashMap<>();
for (TNTPoint record : records) {
int tntId = record.getTntId();
List<TNTPoint> history = histories.computeIfAbsent(tntId, id -> new ArrayList<>());
history.add(record);
record.setHistory(history);
}
this.records = new SoftReference<>(records);
records = new SoftReference<>(TraceRepository.readTraceRecords(this));
}
public synchronized List<TNTPoint> getRecords() {
@@ -360,6 +311,7 @@ public class Trace {
", region=" + region +
", creationTime=" + date +
", recordsSaveFile=" + recordsSaveFile.getName() +
", recordCount=" + recordsCount +
", records=" + getRecords() +
'}';
}
@@ -30,11 +30,14 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginEnableEvent;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
import static de.steamwar.bausystem.features.tracer.TraceRepository.tracesFolder;
@Linked
public class TraceManager implements Listener {
@@ -44,9 +47,9 @@ public class TraceManager implements Listener {
instance = this;
}
public static File tracesFolder = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "traces");
public TraceManager() {
public void init() {
if (!tracesFolder.exists())
tracesFolder.mkdir();
@@ -58,7 +61,15 @@ public class TraceManager implements Listener {
if (traceFile.getName().contains(".records"))
continue;
add(new Trace(traceFile));
if (TraceRepository.getVersion(traceFile) == TraceRepository.SERIALISATION_VERSION) {
add(TraceRepository.readTrace(traceFile));
} else {
String uuid = traceFile.getName().replace(".meta", "");
new File(tracesFolder, uuid + ".records").deleteOnExit();
new File(tracesFolder, uuid + ".meta").deleteOnExit();
}
}
}
@@ -160,8 +171,8 @@ public class TraceManager implements Listener {
tracesByRegion.getOrDefault(region, new HashMap<>())
.forEach((i, trace) -> {
if (trace.getRegion() != region) return;
trace.getMetadataSaveFile().delete();
trace.getRecordsSaveFile().delete();
trace.getMetadataSaveFile().deleteOnExit();
trace.getRecordsSaveFile().deleteOnExit();
});
tracesByRegion.getOrDefault(region, new HashMap<>()).clear();
}
@@ -31,6 +31,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.event.server.PluginEnableEvent;
import java.util.*;
import java.util.logging.Level;
@@ -70,7 +71,7 @@ public class TraceRecorder implements Listener {
*/
private final Set<Region> autoTraceRegions = new HashSet<>();
public TraceRecorder() {
public void init() {
BauSystem.runTaskTimer(BauSystem.getInstance(), () -> {
record();
checkForAutoTraceFinish();
@@ -170,14 +171,13 @@ public class TraceRecorder implements Listener {
if (history.size() == 0) {
try {
historyMap.put(tntPrimed, history);
}
catch (NullPointerException e) {
} catch (NullPointerException e) {
Logger logger = Bukkit.getLogger();
//TODO remove when no longer neccecary
logger.log(Level.WARNING, "Nullpointer thrown by historyMap");
logger.log(Level.WARNING, "TNT History: " + history);
logger.log(Level.WARNING, "History Map: " + historyMap);
throw e;
throw e;
}
tntID = wrappedTrace.getNextOpenRecordIdAndIncrement();
} else {
@@ -24,28 +24,22 @@ import de.steamwar.bausystem.region.Region;
import lombok.Getter;
import lombok.SneakyThrows;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPOutputStream;
public class TraceRecordingWrapper {
@Getter
private final Trace trace;
@Getter
private final long startTick;
private final List<TNTPoint> recordsToAdd;
private final List<TNTPoint> recordList;
private final ObjectOutputStream recordsOutputStream;
private int nextOpenRecordId = 0;
@Getter
private boolean explosionRecorded = false;
@Getter
private final Trace trace;
@SneakyThrows
public TraceRecordingWrapper(Region region) {
startTick = TPSUtils.currentRealTick.get();
@@ -53,8 +47,6 @@ public class TraceRecordingWrapper {
recordList = new ArrayList<>();
trace = new Trace(region, recordList);
File recordsSaveFile = new File(TraceManager.tracesFolder, trace.getUuid() + ".records");
recordsOutputStream = new ObjectOutputStream(new GZIPOutputStream(new FileOutputStream(recordsSaveFile)));
}
public int getNextOpenRecordIdAndIncrement() {
@@ -72,23 +64,14 @@ public class TraceRecordingWrapper {
public void commitRecorded() {
TraceManager.instance.showPartial(trace, recordsToAdd);
recordsToAdd.forEach(record -> {
try {
recordsOutputStream.writeObject(record);
recordsOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
});
recordList.addAll(recordsToAdd);
trace.setRecordsCount(recordList.size());
recordsToAdd.clear();
}
@SneakyThrows
protected void finalizeRecording() {
recordsOutputStream.flush();
recordsOutputStream.close();
TraceRepository.writeTrace(trace, recordList);
TraceManager.instance.add(trace);
}
}
@@ -0,0 +1,136 @@
package de.steamwar.bausystem.features.tracer;
import de.steamwar.bausystem.region.Region;
import lombok.Cleanup;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import java.io.*;
import java.util.*;
public class TraceRepository {
/**
* Increment this when changing serialisation format
*/
public static final int SERIALISATION_VERSION = 1;
public static File tracesFolder = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "traces");
@SneakyThrows
protected static int getVersion(File metadataFile) {
@Cleanup
ObjectInputStream reader = new ObjectInputStream(new FileInputStream(metadataFile));
reader.readUTF();
reader.readUTF();
reader.readObject();
try {
int version = reader.readInt();
return version;
} catch (EOFException e) {
return 0;
}
}
@SneakyThrows
public static Trace readTrace(File metadataFile) {
@Cleanup
ObjectInputStream reader = new ObjectInputStream(new FileInputStream(metadataFile));
UUID uuid = UUID.fromString(reader.readUTF());
Region region = Region.getREGION_MAP().get(reader.readUTF());
Date date = (Date) reader.readObject();
File recordsFile = new File(tracesFolder,uuid + ".records");
int serialisationVersion = reader.readInt();
int recordsCount = reader.readInt();
return new Trace(uuid, region, date, metadataFile, recordsFile, recordsCount);
}
@SneakyThrows
protected static void writeTrace(Trace trace, List<TNTPoint> records) {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(trace.getMetadataSaveFile()));
outputStream.writeUTF(trace.getUuid().toString());
outputStream.writeUTF(trace.getRegion().getName());
outputStream.writeObject(trace.getDate());
outputStream.writeInt(SERIALISATION_VERSION);
outputStream.writeInt(records.size());
outputStream.flush();
outputStream.close();
writeTraceRecords(trace.getRecordsSaveFile(), records);
}
@SneakyThrows
protected static void writeTraceRecords(File recordsFile, List<TNTPoint> records) {
DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(recordsFile));
for (TNTPoint record : records) {
outputStream.writeInt(record.getTntId());
outputStream.writeBoolean(record.isExplosion());
outputStream.writeBoolean(record.isInWater());
outputStream.writeBoolean(record.isAfterFirstExplosion());
outputStream.writeBoolean(record.isDestroyedBuildArea());
outputStream.writeBoolean(record.isDestroyedTestBlock());
outputStream.writeLong(record.getTicksSinceStart());
outputStream.writeInt(record.getFuse());
Location location = record.getLocation();
outputStream.writeDouble(location.getX());
outputStream.writeDouble(location.getY());
outputStream.writeDouble(location.getZ());
Vector velocity = record.getVelocity();
outputStream.writeDouble(velocity.getX());
outputStream.writeDouble(velocity.getY());
outputStream.writeDouble(velocity.getZ());
}
outputStream.flush();
outputStream.close();
}
@SneakyThrows
protected static TNTPoint readTraceRecord(DataInputStream objectInput) {
int tntId = objectInput.readInt();
boolean explosion = objectInput.readBoolean();
boolean inWater = objectInput.readBoolean();
boolean afterFirstExplosion = objectInput.readBoolean();
boolean destroyedBuildArea = objectInput.readBoolean();
boolean destroyedTestBlock = objectInput.readBoolean();
long ticksSinceStart = objectInput.readLong();
int fuse = objectInput.readInt();
double locX = objectInput.readDouble();
double locY = objectInput.readDouble();
double locZ = objectInput.readDouble();
Location location = new Location(Bukkit.getWorlds().get(0), locX, locY, locZ);
double velX = objectInput.readDouble();
double velY = objectInput.readDouble();
double velZ = objectInput.readDouble();
Vector velocity = new Vector(velX, velY, velZ);
return new TNTPoint(tntId, explosion, inWater, afterFirstExplosion, destroyedBuildArea, destroyedTestBlock, ticksSinceStart, fuse, location, velocity, Collections.emptyList());
}
@SneakyThrows
protected static List<TNTPoint> readTraceRecords(Trace trace) {
File recordsFile = trace.getRecordsSaveFile();
@Cleanup
DataInputStream inputStream = new DataInputStream(new FileInputStream(recordsFile));
List<TNTPoint> records = new ArrayList<>();
for (int i = 0; i < trace.getRecordsCount(); i++) {
records.add(readTraceRecord(inputStream));
}
Map<Integer, List<TNTPoint>> histories = new HashMap<>();
for (TNTPoint record : records) {
int tntId = record.getTntId();
List<TNTPoint> history = histories.computeIfAbsent(tntId, id -> new ArrayList<>());
history.add(record);
record.setHistory(history);
}
return records;
}
}
@@ -20,6 +20,7 @@
package de.steamwar.bausystem.features.world;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.CRIUWakeupEvent;
import de.steamwar.core.CheckpointUtils;
import de.steamwar.linkage.Linked;
import org.bukkit.Bukkit;
@@ -35,20 +36,16 @@ import org.bukkit.event.player.PlayerQuitEvent;
@Linked
public class AFKStopperListener implements Listener {
private int afkTicks = 0;
private long lastMovementTime = System.currentTimeMillis();
public AFKStopperListener() {
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> {
switch (afkTicks) {
case 15:
for (Player p : Bukkit.getOnlinePlayers()) {
p.kickPlayer(BauSystem.MESSAGE.parse("AFK_KICK_MESSAGE", p));
}
case 14:
BauSystem.MESSAGE.broadcast("AFK_WARNING_MESSAGE");
default:
afkTicks++;
}
long currentTime = System.currentTimeMillis();
if(currentTime - lastMovementTime > 10*60000) { // 10 Minutes
for (Player p : Bukkit.getOnlinePlayers())
p.kickPlayer(BauSystem.MESSAGE.parse("AFK_KICK_MESSAGE", p));
} else if(currentTime - lastMovementTime > 9*60000)
BauSystem.MESSAGE.broadcast("AFK_WARNING_MESSAGE");
}, 1200, 1200); //every minute
}
@@ -60,7 +57,7 @@ public class AFKStopperListener implements Listener {
Location from = event.getFrom();
if (from.getPitch() != to.getPitch() || from.getYaw() != to.getYaw())
afkTicks = 0;
lastMovementTime = System.currentTimeMillis();
}
@EventHandler(priority = EventPriority.LOWEST) //Potential fix for potential race condition with WE axe spontaneously not working
@@ -73,4 +70,9 @@ public class AFKStopperListener implements Listener {
if(Bukkit.getOnlinePlayers().isEmpty() || (Bukkit.getOnlinePlayers().size() == 1 && Bukkit.getOnlinePlayers().contains(event.getPlayer())))
CheckpointUtils.freeze();
}
@EventHandler
public void onCRIUWakeup(CRIUWakeupEvent event) {
lastMovementTime = System.currentTimeMillis();
}
}
@@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.world;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.config.BauServer;
import de.steamwar.core.CRIUWakeupEvent;
import de.steamwar.linkage.Linked;
import de.steamwar.sql.BauweltMember;
import de.steamwar.sql.SteamwarUser;
@@ -32,7 +33,6 @@ import org.bukkit.event.player.PlayerJoinEvent;
@Linked
public class AntiBauAddMemberFix implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onPlayerJoin(PlayerJoinEvent event) {
if (BauSystem.DEV_SERVER) return;
@@ -44,4 +44,9 @@ public class AntiBauAddMemberFix implements Listener {
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + SteamwarUser.get(BauServer.getInstance().getOwnerID()).getUserName() + " without being added!");
}
}
@EventHandler
public void onCRIUWakeup(CRIUWakeupEvent event) {
BauweltMember.clear();
}
}
@@ -0,0 +1,89 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.bausystem.features.world;
import com.sk89q.worldedit.regions.RegionSelector;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.bausystem.utils.WorldEditUtils;
import de.steamwar.linkage.Linked;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.io.*;
import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors;
@Linked
public class WorldEditSelectionSaver implements Listener {
private File WORLD_EDIT_SELECTIONS = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "world_edit_selections");
{
WORLD_EDIT_SELECTIONS.mkdir();
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
SteamwarUser steamwarUser = SteamwarUser.get(event.getPlayer().getUniqueId());
File file = new File(WORLD_EDIT_SELECTIONS, Integer.toString(steamwarUser.getId()));
if (!file.exists()) return;
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
Class<? extends RegionSelector> clazz = (Class<? extends RegionSelector>) Class.forName(bufferedReader.readLine());
List<Location> locations = bufferedReader.lines()
.map(s -> s.split(" "))
.map(strings -> strings.length != 3 ? null : new Location(event.getPlayer().getWorld(), Integer.parseInt(strings[0]), Integer.parseInt(strings[1]), Integer.parseInt(strings[2])))
.collect(Collectors.toList());
WorldEditUtils.setVertices(event.getPlayer(), clazz, locations);
} catch (IOException | ClassNotFoundException e) {
BauSystem.getInstance().getLogger().log(Level.SEVERE, e.getMessage(), e);
file.delete();
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
SteamwarUser steamwarUser = SteamwarUser.get(event.getPlayer().getUniqueId());
Pair<Class<? extends RegionSelector>, List<Location>> data = WorldEditUtils.getVertices(event.getPlayer());
File file = new File(WORLD_EDIT_SELECTIONS, Integer.toString(steamwarUser.getId()));
try {
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
bufferedWriter.write(data.getKey().getTypeName());
for (Location location : data.getValue()) {
if (location == null) {
bufferedWriter.write("\n");
} else {
bufferedWriter.write("\n" + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ());
}
}
bufferedWriter.close();
} catch (IOException e) {
BauSystem.getInstance().getLogger().log(Level.SEVERE, e.getMessage(), e);
file.delete();
}
}
}
@@ -0,0 +1,49 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.bausystem.utils;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.ViaAPI;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.linkage.Linked;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@Linked
public class VersionAnnouncer implements Listener {
private final String versionString = Bukkit.getBukkitVersion().split("-", 2)[0];
@SuppressWarnings("unchecked")
private final ViaAPI<Player> via = Via.getAPI();
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if(via.getServerVersion().supportedVersions().contains(via.getPlayerVersion(player)))
return;
BauSystem.MESSAGE.sendPrefixless("SERVER_VERSION", player, ChatMessageType.ACTION_BAR, versionString);
}
}
@@ -19,17 +19,21 @@
package de.steamwar.bausystem.utils;
import com.comphenix.tinyprotocol.Reflection;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extension.factory.PatternFactory;
import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
import de.steamwar.bausystem.shared.Pair;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
@@ -37,6 +41,9 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.stream.Collectors;
@UtilityClass
public class WorldEditUtils {
@@ -77,6 +84,40 @@ public class WorldEditUtils {
return WorldEdit.getInstance().getPatternFactory();
}
public Pair<Class<? extends RegionSelector>, List<Location>> getVertices(Player player) {
RegionSelector regionSelector = WorldEdit.getInstance()
.getSessionManager()
.get(BukkitAdapter.adapt(player))
.getRegionSelector(BukkitAdapter.adapt(player.getWorld()));
return new Pair<>(regionSelector.getClass(), regionSelector.getVertices()
.stream()
.map(blockVector3 -> blockVector3 == null ? null : adapt(player.getWorld(), blockVector3))
.collect(Collectors.toList()));
}
public void setVertices(Player player, Class<? extends RegionSelector> clazz, List<Location> vertices) {
LocalSession localSession = WorldEdit.getInstance()
.getSessionManager()
.get(BukkitAdapter.adapt(player));
Reflection.ConstructorInvoker constructorInvoker = Reflection.getConstructor(clazz, com.sk89q.worldedit.world.World.class);
RegionSelector regionSelector = (RegionSelector) constructorInvoker.invoke(BukkitAdapter.adapt(player.getWorld()));
localSession.setRegionSelector(BukkitAdapter.adapt(player.getWorld()), regionSelector);
if (vertices.isEmpty()) return;
SelectorLimits selectorLimits = ActorSelectorLimits.forActor(BukkitAdapter.adapt(player));
for (int i = 0; i < vertices.size(); i++) {
Location location = vertices.get(i);
if (location == null) continue;
if (i == 0) {
regionSelector.selectPrimary(BukkitAdapter.adapt(location).toBlockPoint(), selectorLimits);
} else {
regionSelector.selectSecondary(BukkitAdapter.adapt(location).toBlockPoint(), selectorLimits);
}
}
}
public Pair<Location, Location> getSelection(Player player) {
RegionSelector regionSelector = WorldEdit.getInstance()
.getSessionManager()
+2
View File
@@ -2,6 +2,8 @@ name: BauSystem
authors: [ Lixfel, YoyoNow, Chaoscaot, Zeanon, D4rkr34lm ]
version: "2.0"
depend: [ WorldEdit, SpigotCore ]
softdepend:
- ViaVersion
load: POSTWORLD
main: de.steamwar.bausystem.BauSystem
api-version: "1.13"
+3 -35
View File
@@ -18,17 +18,8 @@
*/
plugins {
id("java")
kotlin("jvm") version "2.0.0"
id("com.github.johnrengelman.shadow")
}
group = "de.steamwar"
version = ""
tasks.compileJava {
options.encoding = "UTF-8"
steamwar.kotlin
alias(libs.plugins.shadow)
}
tasks.build {
@@ -39,29 +30,6 @@ tasks.shadowJar {
exclude("org/**")
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
sourceSets {
main {
java {
srcDirs("src/")
}
resources {
srcDirs("src/")
exclude("**/*.java", "**/*.kt")
}
}
}
dependencies {
implementation(kotlin("reflect"))
compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
compileOnly(libs.paperapi21)
}
kotlin {
jvmToolchain(21)
}
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 SteamWar.de-Serverteam
* Copyright (C) 2024 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
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core
package de.steamwar.kotlin
import org.bukkit.plugin.java.JavaPlugin
+1 -1
View File
@@ -1,5 +1,5 @@
name: KotlinCore
version: '2.0.0'
main: de.steamwar.core.KotlinCore
main: de.steamwar.kotlin.KotlinCore
load: POSTWORLD
api-version: '1.21'
@@ -111,11 +111,11 @@ public abstract class SpecialItem {
}
public static ItemStack getRandomItem() {
if (Config.MissileChance == 0 || consecutiveSupportItems > 1) {
consecutiveSupportItems = 0;
if (Config.MissileChance == 0) {
return supportItems.get(random.nextInt(supportItems.size())).getItem();
}
if (Config.MissileChance == 1) {
if (Config.MissileChance == 1 || consecutiveSupportItems > 1) {
consecutiveSupportItems = 0;
return missileItems.get(random.nextInt(missileItems.size())).getItem();
}
@@ -0,0 +1,36 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.core;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class CRIUSleepEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -0,0 +1,36 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.core;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class CRIUWakeupEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -22,7 +22,6 @@ package de.steamwar.core;
import com.comphenix.tinyprotocol.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import com.viaversion.viaversion.api.Via;
import de.steamwar.sql.BauweltMember;
import de.steamwar.sql.internal.Statement;
import io.netty.channel.ChannelFuture;
import org.bukkit.Bukkit;
@@ -94,6 +93,7 @@ public class CheckpointUtils {
private static final Reflection.FieldAccessor<List> channelFutures = Reflection.getField(TinyProtocol.serverConnection, List.class, 0, ChannelFuture.class);
private static final Reflection.MethodInvoker bind = Reflection.getMethod(TinyProtocol.serverConnection, null, InetAddress.class, int.class);
private static void freezeInternal(Path path) throws Exception {
Bukkit.getPluginManager().callEvent(new CRIUSleepEvent());
Bukkit.getWorlds().forEach(FlatteningWrapper.impl::syncSave);
Statement.closeAll();
@@ -141,7 +141,7 @@ public class CheckpointUtils {
}
Via.getManager().getInjector().inject();
BauweltMember.clear();
Bukkit.getPluginManager().callEvent(new CRIUWakeupEvent());
Core.getInstance().getLogger().log(Level.INFO, "Checkpoint restored");
}
}
+9
View File
@@ -0,0 +1,9 @@
plugins {
steamwar.kotlin
}
dependencies {
compileOnly(libs.paperapi21)
compileOnly(project(":SpigotCore"))
compileOnly(project(":KotlinCore"))
}
+100
View File
@@ -0,0 +1,100 @@
startDelay: 10
gameTime: 1200
prices:
TNT:
price: 4
amount: 4
REDSTONE:
price: 4
amount: 4
REPEATER:
price: 4
amount: 2
COMPARATOR:
price: 4
amount: 1
REDSTONE_BLOCK:
price: 4
amount: 1
REDSTONE_TORCH:
price: 4
amount: 2
END_STONE:
price: 4
amount: 8
ICE:
price: 8
amount: 1
LEVER:
price: 4
amount: 1
OAK_BUTTON:
price: 4
amount: 1
STONE_BUTTON:
price: 4
amount: 1
OAK_TRAPDOOR:
price: 4
amount: 2
IRON_TRAPDOOR:
price: 4
amount: 1
PISTON:
price: 4
amount: 2
STICKY_PISTON:
price: 4
amount: 1
GLASS:
price: 4
amount: 2
OAK_FENCE:
price: 4
amount: 4
LADDER:
price: 6
amount: 2
WHITE_GLAZED_TERRACOTTA:
price: 4
amount: 3
JUKEBOX:
price: 4
amount: 2
OBSERVER:
price: 10
amount: 4
BREWING_STAND:
price: 4
amount: 1
STRING:
price: 4
amount: 2
END_STONE_BRICK_SLAB:
price: 4
amount: 2
TARGET:
price: 4
amount: 1
COPPER_BULB:
price: 4
amount: 1
SLIME_BLOCK:
price: 6
amount: 2
HONEY_BLOCK:
price: 6
amount: 2
STONE_PRESSURE_PLATE:
price: 4
amount: 1
NOTE_BLOCK:
price: 3
amount: 1
HOPPER:
price: 3
amount: 1
GRAVEL:
price: 4
amount: 3
@@ -0,0 +1,48 @@
package de.steamwar.tntleague
import de.steamwar.tntleague.command.AcceptCommand
import de.steamwar.tntleague.command.InviteCommand
import de.steamwar.tntleague.command.LeaveCommand
import de.steamwar.tntleague.command.RemoveCommand
import de.steamwar.tntleague.events.GlobalListener
import de.steamwar.tntleague.events.LobbyListener
import net.kyori.adventure.key.Key
import net.kyori.adventure.translation.GlobalTranslator
import net.kyori.adventure.translation.TranslationRegistry
import net.kyori.adventure.util.UTF8ResourceBundleControl
import org.bukkit.plugin.java.JavaPlugin
import java.util.*
lateinit var plugin: TNTLeague
class TNTLeague : JavaPlugin() {
init {
plugin = this
}
override fun onEnable() {
saveResource("config.yml", false)
saveDefaultConfig()
val registry = TranslationRegistry.create(Key.key("steamwar:tntleague"))
val bundleDe = ResourceBundle.getBundle("de.steamwar.tntleague.TNTLeague", Locale.GERMAN, UTF8ResourceBundleControl())
val bundleEn = ResourceBundle.getBundle("de.steamwar.tntleague.TNTLeague", Locale.US, UTF8ResourceBundleControl())
registry.defaultLocale(Locale.US)
registry.registerAll(Locale.GERMAN, bundleDe, true)
registry.registerAll(Locale.US, bundleEn, true)
GlobalTranslator.translator().addSource(registry)
server.pluginManager.registerEvents(LobbyListener, this)
server.pluginManager.registerEvents(GlobalListener, this)
logger.info("TNTLeague enabled")
InviteCommand.register()
AcceptCommand.register()
RemoveCommand.register()
LeaveCommand.register()
}
}
@@ -0,0 +1,18 @@
join={0} ist dem Spiel beigetreten!
joinTeam={0} ist dem {1} team begetreten!
quit={0} hat das Spiel verlassen!
blue=Blau
red=Rot
shutdown=Der Server fährt in {0} sekunden herunter!
teamWin=Team {0} gewinnt!
notEnoughCoins=Du hast nicht genug Coins um dir das zu kaufen!
gameStarting=Das Spiel beginnt in {0} Sekunden!
gameStart=Start in {0}
gameStarted=Das Spiel beginnt!
gameEnded=Das Spiel ist aus!
dealer=Händler
dealerItem=
dealerPrice=Kosten: {0} Coins
scoreboardTarget=Ziel: {0}
scoreboardTime=Zeit: {0}:{1}
scoreboardTeam=
@@ -0,0 +1,37 @@
join={0} joined the game!
joinTeam={0} joined the {1} team!
quit={0} left the game!
quitTeam={0} left the {1} team!
blue=Blue
red=Red
shutdown=The server stops in {0} seconds!
teamWin=Team {0} wins!
notEnoughCoins=You don't have enough coins to buy this item!
gameStarting=The game starts in {0} seconds!
gameStart=Starting in {0}
gameStarted=The game has started!
timeRemaining={0} minutes remaining!
gameEnded=The game has ended!
draw=The game ended in a draw!
chat={0}» {1}
dealer=Shopkeeper
dealerItem={0} {1}
dealerPrice=Price: {0} Coins
scoreboardTarget=Target: {0}
scoreboardTime=Time: {0}:{1}
scoreboardTeam=Team {0}: {1}
ready=Ready
notReady=Not ready
isReady=Team {0} is ready!
isNotReady=Team {0} is not ready!
invited={0} invited you to join the {1} team! *Click*
invitedHover=Click to join the {0} team!
invitedPlayer=Invited {0} to join your team!
@@ -0,0 +1,40 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tntleague.command
import de.steamwar.command.SWCommand
import de.steamwar.tntleague.game.TNTLeagueGame
import org.bukkit.entity.Player
object AcceptCommand: SWCommand("accept") {
@Register
fun acceptInvite(sender: Player, @Validator("isLeader") target: Player) {
if (TNTLeagueGame.state != TNTLeagueGame.GameState.LOBBY) return
val team = TNTLeagueGame.getTeam(target) ?: return
if (team.leader != target) return
if (sender !in team.invites) return
team.invites.remove(target)
team.opposite.invites.remove(target)
team.join(sender)
}
}
@@ -0,0 +1,56 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tntleague.command
import de.steamwar.command.SWCommand
import de.steamwar.command.TypeValidator
import de.steamwar.tntleague.game.TNTLeagueGame
import de.steamwar.tntleague.util.*
import net.kyori.adventure.text.event.ClickEvent
import net.kyori.adventure.text.event.HoverEvent
import org.bukkit.entity.Player
object InviteCommand: SWCommand("invite") {
@Register
fun invitePlayer(@Validator("isLeader") sender: Player, target: Player) {
if (TNTLeagueGame.state != TNTLeagueGame.GameState.LOBBY) return
if (TNTLeagueGame.getTeam(target) != null) return
val team = TNTLeagueGame.getTeam(sender)!!
team.invites.add(target)
target.sendMessage(translate("invited", sender.name.yellow(), translate(team.name).colorByTeam(team)).basic().clickEvent(
ClickEvent.callback {
if (target !in team.invites) return@callback
team.invites.remove(target)
team.opposite.invites.remove(target)
team.join(target)
})
.hoverEvent(HoverEvent.showText(translate("invitedHover", translate(team.name).colorByTeam(team)).green())))
sender.sendMessage(translate("invitedPlayer", target.name.yellow()).basic())
}
@Validator("isLeader", local = false)
fun isLeader(): TypeValidator<Player> {
return TypeValidator<Player> { _, player, _ -> TNTLeagueGame.getTeam(player)?.leader == player}
}
}
@@ -0,0 +1,30 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tntleague.command
import de.steamwar.command.SWCommand
import de.steamwar.tntleague.game.TNTLeagueGame
import org.bukkit.entity.Player
object LeaveCommand: SWCommand("leave", "l") {
@Register
fun leave(player: Player) = TNTLeagueGame.getTeam(player)?.remove(player)
}
@@ -0,0 +1,39 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tntleague.command
import de.steamwar.command.SWCommand
import de.steamwar.tntleague.game.TNTLeagueGame
import org.bukkit.entity.Player
object RemoveCommand: SWCommand("remove") {
@Register
fun removePlayer(@Validator("isLeader") sender: Player, target: Player) {
if (TNTLeagueGame.state != TNTLeagueGame.GameState.LOBBY) return
if (sender == target) return
val team = TNTLeagueGame.getTeam(sender) ?: return
if (team.leader != sender) return
if (target !in team.members) return
team.remove(target)
}
}
@@ -0,0 +1,35 @@
package de.steamwar.tntleague.config
import de.steamwar.tntleague.plugin
import org.bukkit.Material
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.configuration.file.FileConfiguration
data class TNTLeagueConfig(
val startDelay: Int = 10,
val gameTime: Int = 60 * 20,
val prices: Map<Material, Price>
) {
companion object {
val config: TNTLeagueConfig by lazy { loadConfig(plugin.config) }
private fun loadConfig(config: FileConfiguration): TNTLeagueConfig {
return TNTLeagueConfig(config.getInt("startDelay"), config.getInt("gameTime"), loadPrices(config.getConfigurationSection("prices")!!))
}
private fun loadPrices(config: ConfigurationSection): Map<Material, Price> {
return config.getKeys(false).associateWith {
Price(
config.getInt("$it.amount"),
config.getInt("$it.price")
)
}.mapKeys { Material.getMaterial(it.key)!! }
}
}
data class Price(
val amount: Int,
val price: Int,
)
}
@@ -0,0 +1,75 @@
package de.steamwar.tntleague.config
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.Area
import de.steamwar.tntleague.util.translate
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.configuration.file.YamlConfiguration
import org.bukkit.entity.Villager
import org.bukkit.entity.WanderingTrader
import java.io.File
val world by lazy { plugin.server.worlds.first()!! }
private val targetedBlocksRed by lazy { TNTLeagueWorldConfig.redTeam.target.blocks.count { block -> block.type == TNTLeagueWorldConfig.targetMaterial } }
private val targetedBlocksBlue by lazy { TNTLeagueWorldConfig.blueTeam.target.blocks.count { block -> block.type == TNTLeagueWorldConfig.targetMaterial } }
private val targetedBlocksAll: Int
get() = if (targetedBlocksBlue == targetedBlocksRed) targetedBlocksBlue else error("Targeted blocks are not equal")
val targetedBlocks: Int
get() = if (TNTLeagueWorldConfig.target != -1) TNTLeagueWorldConfig.target else targetedBlocksAll
object TNTLeagueWorldConfig {
private val config: YamlConfiguration by lazy {
YamlConfiguration.loadConfiguration(
File(
plugin.server.worlds.first().worldFolder,
"tntleague.yml"
)
)
}
val blueTeam: TeamConfig = TeamConfig.fromConfig(config.getConfigurationSection("blueTeam")!!)
val redTeam: TeamConfig = TeamConfig.fromConfig(config.getConfigurationSection("redTeam")!!)
val lobby: Location = config.getLocation("lobby", blueTeam.spawnLocation.clone().add(redTeam.spawnLocation).multiply(0.5))!!
val targetMaterial: Material = Material.matchMaterial(config.getString("targetMaterial", "IRON_BLOCK")!!)!!
val minHeight: Int = config.getInt("minHeight", 0)
val target: Int = config.getInt("target", -1)
@JvmRecord
data class TeamConfig(
val spawnLocation: Location,
val dealerSpawn: Location,
val itemSpawn: Location,
val target: Area
) {
companion object {
fun fromConfig(config: ConfigurationSection): TeamConfig {
val spawnLocation = config.getLocation("spawn")!!
val dealerSpawn = config.getLocation("dealerSpawn")!!
val itemSpawn = config.getLocation("itemSpawn")!!
val targetPos1 = config.getLocation("targetMin")!!
val targetPos2 = config.getLocation("targetMax")!!
spawnDealer(dealerSpawn)
return TeamConfig(spawnLocation, dealerSpawn, itemSpawn, Area(targetPos1, targetPos2))
}
private fun spawnDealer(loc: Location) = world.spawn(loc, WanderingTrader::class.java)
.apply {
customName(translate("dealer"))
isCustomNameVisible = false
isInvulnerable = true
isSilent = true
isCollidable = false
isAware = false
setAI(false)
}
}
}
}
@@ -0,0 +1,25 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tntleague.events
import org.bukkit.event.Listener
object DummyListener: Listener {
}
@@ -0,0 +1,88 @@
package de.steamwar.tntleague.events
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
import de.steamwar.tntleague.game.TNTLeagueGame
import de.steamwar.tntleague.game.TNTLeagueTeam
import de.steamwar.tntleague.inventory.SWInventoryHolder
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.*
import io.papermc.paper.event.player.AsyncChatEvent
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.event.player.PlayerRespawnEvent
object GlobalListener: Listener {
@EventHandler(priority = EventPriority.LOW)
fun onPlayerJoin(e: PlayerJoinEvent) {
e.joinMessage(null)
with(e.player) {
teleport(TNTLeagueWorldConfig.lobby)
inventory.clear()
plugin.server.broadcast(translate("join", name.bold()).basic())
isOp = false
gameMode = GameMode.SPECTATOR
respawnLocation = TNTLeagueWorldConfig.lobby
}
}
@EventHandler(priority = EventPriority.HIGHEST)
fun onPlayerQuit(e: PlayerQuitEvent) {
e.quitMessage(null)
plugin.server.broadcast(translate("quit", e.player.name.bold().colorByTeam(TNTLeagueGame.getTeam(e.player))).basic())
TNTLeagueGame.playerLeave(e.player)
}
@EventHandler(priority = EventPriority.LOWEST)
fun onPlayerClick(e: InventoryClickEvent) {
val holder = e.inventory.getHolder(false)
if (holder is SWInventoryHolder && e.clickedInventory == holder._inventory) {
e.isCancelled = true
holder.handleInventoryClick(e)
}
}
@EventHandler(priority = EventPriority.LOWEST)
fun onPlayerMove(e: PlayerMoveEvent) {
if (e.to.blockY < TNTLeagueWorldConfig.minHeight) {
when (val team = TNTLeagueGame.getTeam(e.player)) {
is TNTLeagueTeam -> e.player.teleport(team.config.spawnLocation)
null -> e.player.teleport(TNTLeagueWorldConfig.blueTeam.spawnLocation)
}
}
e.player.foodLevel = 20
e.player.saturation = 20f
}
@EventHandler
fun onPlayerDeath(e: PlayerDeathEvent) {
e.deathMessage(null)
e.drops.clear()
e.itemsToKeep.removeIf { it.type != Material.DIAMOND_PICKAXE }
}
@EventHandler
fun onPlayerRespawn(e: PlayerRespawnEvent) {
when (val team = TNTLeagueGame.getTeam(e.player)) {
is TNTLeagueTeam -> e.respawnLocation = team.config.spawnLocation
null -> e.respawnLocation = TNTLeagueWorldConfig.lobby
}
}
@EventHandler
fun onChat(e: AsyncChatEvent) {
e.renderer { source, sourceDisplayName, message, _ ->
translate("chat", sourceDisplayName.colorByTeam(TNTLeagueGame.getTeam(source)), message).basic()
}
}
}
@@ -0,0 +1,77 @@
package de.steamwar.tntleague.events
import de.steamwar.scoreboard.SWScoreboard
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
import de.steamwar.tntleague.game.TNTLeagueGame
import de.steamwar.tntleague.game.TNTLeagueTeam
import de.steamwar.tntleague.inventory.DealerInventory
import de.steamwar.tntleague.util.TNTLeagueScoreboard
import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.entity.EntityType
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.entity.EntityExplodeEvent
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.player.PlayerDropItemEvent
import org.bukkit.event.player.PlayerInteractEntityEvent
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.event.player.PlayerQuitEvent
object IngameListener: Listener {
@EventHandler
fun onEntityInteract(e: PlayerInteractEntityEvent) {
if (e.player.gameMode == GameMode.SPECTATOR) return
if(e.rightClicked.type == EntityType.VILLAGER) {
e.isCancelled = true
e.player.openInventory(DealerInventory(e.player).getInventory())
}
}
@EventHandler
fun onExplode(e: EntityExplodeEvent) {
e.blockList().filter { it.type == TNTLeagueWorldConfig.targetMaterial }
.groupBy { getTeamByTargetLocation(it.location) }
.filterKeysNotNull()
.mapValues { it.value.size }
.forEach { it.key.damagedBlocks += it.value }
}
@EventHandler
fun onJoin(e: PlayerJoinEvent) {
SWScoreboard.createScoreboard(e.player, TNTLeagueScoreboard(e.player))
}
@EventHandler
fun onMove(e: PlayerMoveEvent) {
if (TNTLeagueGame.getTeam(e.player) != null) {
if (e.to.blockX >= TNTLeagueWorldConfig.lobby.blockX && e.to.blockX <= TNTLeagueWorldConfig.lobby.blockX + 1 ||
e.to.blockZ >= TNTLeagueWorldConfig.lobby.blockZ && e.to.blockZ <= TNTLeagueWorldConfig.lobby.blockZ + 1) {
e.isCancelled = true
}
}
}
@EventHandler
fun onDropPickaxe(e: PlayerDropItemEvent) {
if (e.itemDrop.itemStack.type == Material.DIAMOND_PICKAXE) {
e.isCancelled = true
}
}
private fun getTeamByTargetLocation(location: Location): TNTLeagueTeam? =
when (location) {
in TNTLeagueWorldConfig.redTeam.target -> TNTLeagueGame.redTeam
in TNTLeagueWorldConfig.blueTeam.target -> TNTLeagueGame.blueTeam
else -> null
}
private fun <K, T> Map<K?, T>.filterKeysNotNull(destination: MutableMap<K, T> = mutableMapOf()): Map<K, T> {
this.forEach { (t, u) -> if(t != null) destination[t] = u }
return destination
}
}
@@ -0,0 +1,61 @@
package de.steamwar.tntleague.events
import de.steamwar.tntleague.game.TNTLeagueGame
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.basic
import de.steamwar.tntleague.util.colorByTeam
import de.steamwar.tntleague.util.translate
import de.steamwar.tntleague.util.yellow
import io.papermc.paper.util.Tick
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.block.Action
import org.bukkit.event.entity.EntityDamageEvent
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.player.PlayerDropItemEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent
object LobbyListener: Listener {
@EventHandler
fun onPlayerJoin(e: PlayerJoinEvent) {
TNTLeagueGame.getFreeTeam()?.run {
join(e.player)
TNTLeagueGame.checkStart()
}
}
@EventHandler(priority = EventPriority.LOWEST)
fun onPlayerQuit(e: PlayerQuitEvent) {
val team = TNTLeagueGame.getTeam(e.player) ?: return
team.leave(e.player)
}
@EventHandler
fun onPlayerDamage(e: EntityDamageEvent) {
e.isCancelled = true
}
@EventHandler
fun onDropEvent(e: PlayerDropItemEvent) {
e.isCancelled = true
}
@EventHandler
fun interactEvent(e: PlayerInteractEvent) {
val team = TNTLeagueGame.getTeam(e.player)
if (e.action.isRightClick && team != null && e.item?.isSimilar(team.readyItem()) == true && team.opposite.leader != null) {
team.isReady = !team.isReady
}
}
@EventHandler
fun inventoryClick(e: InventoryClickEvent) {
if (e.clickedInventory == e.whoClicked.inventory) {
e.isCancelled = true
}
}
}
@@ -0,0 +1,259 @@
package de.steamwar.tntleague.game
import de.steamwar.scoreboard.SWScoreboard
import de.steamwar.sql.Fight
import de.steamwar.sql.FightPlayer
import de.steamwar.sql.SteamwarUser
import de.steamwar.tntleague.config.TNTLeagueConfig
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
import de.steamwar.tntleague.config.world
import de.steamwar.tntleague.events.DummyListener
import de.steamwar.tntleague.events.IngameListener
import de.steamwar.tntleague.events.LobbyListener
import de.steamwar.tntleague.inventory.DealerInventory
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.*
import net.kyori.adventure.bossbar.BossBar
import net.kyori.adventure.sound.Sound
import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.block.data.type.TNT
import org.bukkit.entity.Entity
import org.bukkit.entity.Item
import org.bukkit.entity.Player
import org.bukkit.entity.TNTPrimed
import org.bukkit.entity.Villager
import org.bukkit.event.HandlerList
import org.bukkit.event.Listener
import org.bukkit.inventory.ItemStack
import org.bukkit.scheduler.BukkitTask
import java.sql.Timestamp
import java.time.Instant
object TNTLeagueGame {
var state: GameState = GameState.LOBBY
set(value) {
if (field.listener != value.listener) {
HandlerList.unregisterAll(field.listener)
plugin.server.pluginManager.registerEvents(value.listener, plugin)
}
field = value
}
var gameTimeRemaining: Int = TNTLeagueConfig.config.gameTime
val blueTeam = TNTLeagueTeam(TNTLeagueWorldConfig.blueTeam, TNTLeagueTeam.Team.BLUE)
val redTeam = TNTLeagueTeam(TNTLeagueWorldConfig.redTeam, TNTLeagueTeam.Team.RED)
private lateinit var start: Timestamp
private var task: Int? = null
private lateinit var spawnerTask: BukkitTask
private lateinit var timerTask: BukkitTask
private fun setup() {
assert(state == GameState.STARTING) { "Game is already running" }
state = GameState.RUNNING
plugin.server.onlinePlayers.forEach { SWScoreboard.createScoreboard(it, TNTLeagueScoreboard(it)) }
blueTeam.start()
redTeam.start()
plugin.server.broadcast(translate("gameStarted").success())
val tnt = ItemStack(Material.TNT)
start = Timestamp.from(Instant.now())
spawnerTask = plugin.server.scheduler.runTaskTimer(plugin, bukkit {
if (world.getNearbyEntitiesByType(Item::class.java, TNTLeagueWorldConfig.blueTeam.itemSpawn, 3.0).sumOf { it.itemStack.amount } <= 256) {
spawnItems(TNTLeagueWorldConfig.blueTeam.itemSpawn, tnt)
spawnItems(TNTLeagueWorldConfig.blueTeam.itemSpawn, DealerInventory.coins)
}
if (world.getNearbyEntitiesByType(Item::class.java, TNTLeagueWorldConfig.redTeam.itemSpawn, 3.0).sumOf { it.itemStack.amount } <= 256) {
spawnItems(TNTLeagueWorldConfig.redTeam.itemSpawn, tnt)
spawnItems(TNTLeagueWorldConfig.redTeam.itemSpawn, DealerInventory.coins)
}
}, 5, 10)
timerTask = plugin.server.scheduler.runTaskTimer(plugin, bukkit {
gameTimeRemaining--
if (gameTimeRemaining == 0) {
draw(WinReason.TIMEOUT)
return@bukkit
}
if (gameTimeRemaining % 300 == 0) {
plugin.server.broadcast(translate("timeRemaining", (gameTimeRemaining / 60).toString().yellow()).basic())
plugin.server.onlinePlayers.forEach { it.playSound(Sound.sound(org.bukkit.Sound.BLOCK_NOTE_BLOCK_PLING.key, Sound.Source.MASTER, 1f, 1f)) }
}
}, 20, 20)
}
private fun bukkit(f: () -> Unit): () -> Unit = f
private fun end() {
if(state != GameState.RUNNING) return
state = GameState.END
plugin.server.onlinePlayers.forEach {
it.gameMode = GameMode.SPECTATOR
SWScoreboard.removeScoreboard(it)
it.playSound(Sound.sound(org.bukkit.Sound.ENTITY_ENDER_DRAGON_DEATH.key, Sound.Source.MASTER, 1f, 1f))
}
plugin.server.broadcast(translate("gameEnded").success())
spawnerTask.cancel()
var shutdown = 10
plugin.server.scheduler.runTaskTimer(plugin, bukkit {
if (shutdown == 0) {
plugin.server.shutdown()
}
plugin.server.broadcast(translate("shutdown", shutdown.toString().yellow()).basic())
shutdown--
}, 20, 20)
}
private fun spawnItems(loc: Location, item: ItemStack) = plugin.server.worlds.first().dropItem(loc, item)
fun getTeam(player: Player) = if (player in blueTeam.members) blueTeam else if (player in redTeam.members) redTeam else null
fun getFreeTeam() = if (blueTeam.leader == null) blueTeam else if (redTeam.leader == null) redTeam else null
fun checkStart() {
if (blueTeam.isReady && redTeam.isReady) {
blueTeam.leader?.inventory?.clear()
redTeam.leader?.inventory?.clear()
state = GameState.STARTING
var countdown = TNTLeagueConfig.config.startDelay
plugin.server.broadcast(translate("gameStarting", countdown.toString().yellow()).basic())
val bar = BossBar.bossBar(translate("gameStart", countdown.toString().yellow()).gray(), (TNTLeagueConfig.config.startDelay - countdown) / TNTLeagueConfig.config.startDelay.toFloat(), BossBar.Color.GREEN, BossBar.Overlay.NOTCHED_10)
plugin.server.onlinePlayers.forEach { bar.addViewer(it) }
task = plugin.server.scheduler.scheduleSyncRepeatingTask(plugin, {
plugin.server.onlinePlayers.forEach { it.playSound(Sound.sound(org.bukkit.Sound.ENTITY_EXPERIENCE_ORB_PICKUP.key, Sound.Source.MASTER, 1f, 1f)) }
if (countdown-- == 0) {
plugin.server.onlinePlayers.forEach { it.hideBossBar(bar) }
task = task?.also { plugin.server.scheduler.cancelTask(it) }.let { null }
setup()
} else {
bar.name(translate("gameStart", countdown.toString().yellow()).gray())
bar.progress((TNTLeagueConfig.config.startDelay - countdown) / TNTLeagueConfig.config.startDelay.toFloat())
plugin.server.onlinePlayers.filter { !it.activeBossBars().contains(bar) }.forEach { bar.addViewer(it) }
}
}, 20, 20)
if (task == -1) {
error("Failed to start countdown task")
}
}
}
fun playerLeave(player: Player) {
blueTeam.invites.remove(player)
redTeam.invites.remove(player)
getTeam(player)?.apply {
members.remove(player)
if (leader == player) {
win(this.opposite, WinReason.LEAVE)
}
}
}
fun reset() {
assert(state == GameState.LOBBY || state == GameState.STARTING) { "Game is not in lobby or starting state" }
if (state == GameState.STARTING) {
task = task?.also { plugin.server.scheduler.cancelTask(it) }.let { null }
plugin.server.onlinePlayers.forEach { p -> p.activeBossBars().forEach { it.removeViewer(p) } }
state = GameState.LOBBY
}
}
fun win(tntLeagueTeam: TNTLeagueTeam, reason: WinReason) {
if (state != GameState.RUNNING) return
end()
plugin.server.broadcast(translate("teamWin", translate(tntLeagueTeam.name).color(tntLeagueTeam.color)).success())
statistic(tntLeagueTeam, reason)
explode(tntLeagueTeam.opposite)
}
fun draw(reason: WinReason) {
if (state != GameState.RUNNING) return
end()
plugin.server.broadcast(translate("draw").success())
statistic(null, reason)
}
fun explode(team: TNTLeagueTeam) {
Area(team.config.spawnLocation.clone().add(20.0, 30.0, 20.0), team.config.spawnLocation.clone().subtract(20.0, 0.0, 20.0).add(0.0, 30.0, 0.0))
.locations
.filterIndexed { index, _ -> index % 7 == 0 }
.forEachIndexed { index, location ->
world.spawn(location, TNTPrimed::class.java).apply {
fuseTicks = index + 40
}
}
}
private fun statistic(winTeam: TNTLeagueTeam?, reason: WinReason) {
val fightId = Fight.create(
"TNTLeague",
world.name,
start,
TNTLeagueConfig.config.gameTime - gameTimeRemaining,
SteamwarUser.get(blueTeam.leader!!.uniqueId).id,
SteamwarUser.get(redTeam.leader!!.uniqueId).id,
null,
null,
when (winTeam) {
blueTeam -> 1
redTeam -> 2
else -> 0
},
when (reason) {
WinReason.TIMEOUT -> "TIMEOUT"
WinReason.DESTROYED -> "DESTROYED"
WinReason.LEAVE -> "LEAVE"
}
)
addTeamMember(blueTeam, fightId)
addTeamMember(redTeam, fightId)
}
private fun addTeamMember(team: TNTLeagueTeam, fightId: Int) {
team.members.filter { team.leader != it }
.forEach {
FightPlayer.create(
fightId,
SteamwarUser.get(it.uniqueId).id,
team == blueTeam,
"TNTLeague",
0,
false
)
}
}
enum class GameState(val listener: Listener) {
LOBBY(LobbyListener),
STARTING(LobbyListener),
RUNNING(IngameListener),
END(DummyListener);
}
enum class WinReason {
TIMEOUT,
DESTROYED,
LEAVE
}
}
@@ -0,0 +1,146 @@
package de.steamwar.tntleague.game
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
import de.steamwar.tntleague.config.targetedBlocks
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.*
import net.kyori.adventure.sound.Sound
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextColor
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.awt.Color.green
data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private val team: Team) {
var leader: Player? = null
set(player) {
field = player
if (player != null) {
with(player.inventory) {
clear()
setItem(4, readyItem())
}
}
}
val members = mutableListOf<Player>()
val invites = mutableListOf<Player>()
val name: String
get() = team.name.lowercase()
val color: TextColor
get() = team.color
var isReady: Boolean = false
set(value) {
field = value
leader?.inventory?.setItem(4, readyItem())
leader?.playSound(Sound.sound(org.bukkit.Sound.BLOCK_NOTE_BLOCK_PLING.key, Sound.Source.MASTER, 1f, 1f))
plugin.server.onlinePlayers.forEach { it.sendActionBar(translate(if (value) "isReady" else "isNotReady", translate(this.name).colorByTeam(this)).let { cmp ->
if (value) {
cmp.green()
} else {
cmp.red()
}
}) }
if (value && opposite.isReady) {
TNTLeagueGame.checkStart()
}
}
var damagedBlocks: Int = 0
set(value) {
field = value
if (value >= targetedBlocks) {
TNTLeagueGame.win(this, TNTLeagueGame.WinReason.DESTROYED)
}
}
val opposite: TNTLeagueTeam
get() = when (team) {
Team.BLUE -> TNTLeagueGame.redTeam
Team.RED -> TNTLeagueGame.blueTeam
}
fun join(player: Player): Boolean {
members.add(player)
with(player) {
teleport(config.spawnLocation)
gameMode = GameMode.ADVENTURE
inventory.clear()
plugin.server.broadcast(translate("joinTeam", name().colorByTeam(this@TNTLeagueTeam), translate(this@TNTLeagueTeam.name).colorByTeam(this@TNTLeagueTeam)).basic())
}
if (leader == null) {
leader = player
}
return true
}
fun readyItem() = if (isReady) {
ItemStack.of(Material.LIME_DYE).apply {
itemMeta = itemMeta.apply {
displayName(translate("ready").green().translate(leader!!))
}
}
} else {
ItemStack.of(Material.RED_DYE).apply {
itemMeta = itemMeta.apply {
displayName(translate("notReady").red().translate(leader!!))
}
}
}
fun start() = members.forEach {
with(it) {
gameMode = GameMode.SURVIVAL
inventory.addItem(ItemStack.of(Material.DIAMOND_PICKAXE).apply {
itemMeta = itemMeta.apply {
isUnbreakable = true
addEnchant(Enchantment.EFFICIENCY, 1, false)
}
})
}
}
fun leave(player: Player) {
if (TNTLeagueGame.state == TNTLeagueGame.GameState.RUNNING) {
TNTLeagueGame.playerLeave(player)
} else {
members.remove(player)
if (members.isEmpty()) {
plugin.server.onlinePlayers.firstOrNull { it != player && TNTLeagueGame.getTeam(it) == null }?.run {
members.add(this)
}
}
if (leader == player) {
leader = members.firstOrNull()
}
}
}
fun remove(player: Player) {
leave(player)
with(player) {
teleport(TNTLeagueWorldConfig.lobby)
gameMode = GameMode.SPECTATOR
inventory.clear()
plugin.server.broadcast(translate("quitTeam", name().colorByTeam(this@TNTLeagueTeam), translate(this@TNTLeagueTeam.name).colorByTeam(this@TNTLeagueTeam)).basic())
}
}
enum class Team(val color: TextColor) {
BLUE(NamedTextColor.BLUE),
RED(NamedTextColor.RED);
}
}
@@ -0,0 +1,75 @@
package de.steamwar.tntleague.inventory
import de.steamwar.tntleague.config.TNTLeagueConfig
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.*
import net.kyori.adventure.sound.Sound
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.Style
import net.kyori.adventure.text.format.TextDecoration
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
import java.util.*
import kotlin.math.ceil
class DealerInventory(player: Player): SWInventoryHolder() {
init {
items.forEachIndexed { index, item ->
this[index] = item.first to {
val price = item.second.price * if (it.isShiftClick) 5 else 1
val amount = item.second.amount * if (it.isShiftClick) 5 else 1
if (!player.inventory.containsAtLeast(coins, price)) {
player.sendMessage(translate("notEnoughCoins").error())
player.playSound(Sound.sound(org.bukkit.Sound.ENTITY_VILLAGER_HURT.key, net.kyori.adventure.sound.Sound.Source.MASTER, 1f, 1f))
return@to
}
player.inventory.removeItem(coins.asQuantity(price))
player.inventory.addItem(ItemStack.of(item.first.type, amount))
}
}
}
override fun createInventory(): Inventory = plugin.server.createInventory(this, ceil(TNTLeagueConfig.config.prices.size / 9f).toInt() * 9, translate("dealer").reset())
companion object {
private val priceKey = NamespacedKey(plugin, "price")
private val amountKey = NamespacedKey(plugin, "amount")
private val coinKey = NamespacedKey(plugin, "coin")
val coins = ItemStack(Material.RAW_GOLD).apply {
itemMeta = itemMeta.apply {
displayName(Component.text("Coins").bold().gold())
persistentDataContainer.apply {
set(coinKey, PersistentDataType.BOOLEAN, true)
}
}
}
val items by lazy {
val prices = TNTLeagueConfig.config.prices
prices.map { (material, price) ->
ItemStack(material).apply {
itemMeta = itemMeta.apply {
displayName(material.name.lowercase().replace("_", " ")
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
.component().gray().appendSpace().append(price.amount.toString().component().yellow()))
amount = price.amount
lore(listOf(price.price.toString().component().yellow().bold().appendSpace().append(Component.text("Coins").yellow())))
persistentDataContainer.apply {
set(priceKey, PersistentDataType.INTEGER, price.price)
set(amountKey, PersistentDataType.INTEGER, price.amount)
}
}
} to price
}
}
}
}
@@ -0,0 +1,41 @@
package de.steamwar.tntleague.inventory
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.InventoryHolder
import org.bukkit.inventory.ItemStack
abstract class SWInventoryHolder: InventoryHolder {
val _inventory: Inventory by lazy { createInventory() }
private val callbacks = mutableMapOf<Int, (event: InventoryClickEvent) -> Unit>()
override fun getInventory(): Inventory = _inventory
abstract fun createInventory(): Inventory
open fun handleInventoryClick(event: InventoryClickEvent) {
callbacks[event.slot]?.invoke(event)
}
fun addItem(item: ItemStack, slot: Int, callback: (event: InventoryClickEvent) -> Unit) {
_inventory.setItem(slot, item)
addCallback(slot, callback)
}
fun addCallback(slot: Int, callback: (event: InventoryClickEvent) -> Unit) {
callbacks[slot] = callback
}
open fun handleClose(event: InventoryCloseEvent) { }
operator fun set(slot: Int, item: Pair<ItemStack, (event: InventoryClickEvent) -> Unit>) {
addItem(item.first, slot, item.second)
}
operator fun set(slot: Int, item: ItemStack) {
addItem(item, slot) { }
}
}
@@ -0,0 +1,46 @@
package de.steamwar.tntleague.util
import org.bukkit.Location
import org.bukkit.block.Block
class Area(loc1: Location, loc2: Location) {
val min: Location
val max: Location
init {
require(loc1.world == loc2.world) { "Locations must be in the same world" }
this.min = loc1 min loc2
this.max = loc1 max loc2
}
operator fun contains(loc: Location): Boolean {
return loc.world == min.world && loc.x >= min.x && loc.x <= max.x && loc.y >= min.y && loc.y <= max.y && loc.z >= min.z && loc.z <= max.z
}
val blocks: Sequence<Block>
inline get() = sequence {
for (x in locations) {
yield(x.block)
}
}
val locations: Sequence<Location>
inline get() = sequence {
for (x in min.blockX..max.blockX) {
for (y in min.blockY..max.blockY) {
for (z in min.blockZ..max.blockZ) {
yield(Location(min.world, x.toDouble(), y.toDouble(), z.toDouble()))
}
}
}
}
}
infix fun Location.max(other: Location): Location {
return Location(world, x.coerceAtLeast(other.x), y.coerceAtLeast(other.y), z.coerceAtLeast(other.z))
}
infix fun Location.min(other: Location): Location {
return Location(world, x.coerceAtMost(other.x), y.coerceAtMost(other.y), z.coerceAtMost(other.z))
}
@@ -0,0 +1,73 @@
package de.steamwar.tntleague.util
import de.steamwar.tntleague.game.TNTLeagueTeam
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.ComponentLike
import net.kyori.adventure.text.TranslatableComponent
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.Style
import net.kyori.adventure.text.format.TextDecoration
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
import net.kyori.adventure.translation.GlobalTranslator
import org.bukkit.entity.Player
import java.util.Locale
val prefix = Component.text("Steam").yellow()
.append(Component.text("War").darkGray())
.appendSpace()
val tntLeaguePrefix = Component.text("TNT").color(NamedTextColor.DARK_RED)
.append(Component.text("League").color(NamedTextColor.GOLD))
val tntLeagueChatPrefix: Component = tntLeaguePrefix
.append(Component.text("»").darkGray())
.appendSpace()
fun TranslatableComponent.basic(): Component = tntLeagueChatPrefix.append(this.gray())
fun TranslatableComponent.error(): Component = tntLeagueChatPrefix.append(this.red())
fun TranslatableComponent.success(): Component = tntLeagueChatPrefix.append(this.green())
fun String.component(): Component = Component.text(this)
fun Component.bold(): Component = this.decorate(TextDecoration.BOLD)
fun String.bold(): Component = this.component().bold()
fun Component.yellow(): Component = this.color(NamedTextColor.YELLOW)
fun String.yellow(): Component = this.component().yellow()
fun Component.red(): Component = this.color(NamedTextColor.RED)
fun String.red(): Component = this.component().red()
fun Component.green(): Component = this.color(NamedTextColor.GREEN)
fun String.green(): Component = this.component().green()
fun Component.gray(): Component = this.color(NamedTextColor.GRAY)
fun String.gray(): Component = this.component().gray()
fun Component.darkGray(): Component = this.color(NamedTextColor.DARK_GRAY)
fun String.darkGray(): Component = this.component().darkGray()
fun Component.gold(): Component = this.color(NamedTextColor.GOLD)
fun translate(key: String, vararg args: ComponentLike): TranslatableComponent = Component.translatable(key, *args).decoration(TextDecoration.ITALIC, false)
fun Component.reset(): Component = this.style(Style.empty())
fun Component.colorByTeam(team: TNTLeagueTeam?) = when (team) {
null -> this.gray()
else -> this.color(team.color)
}
fun Component.translate(locale: Locale): Component = GlobalTranslator.render(this, locale)
fun Component.translate(p: Player): Component = this.translate(p.locale())
fun Component.toLegacy(): String = LegacyComponentSerializer.legacySection().serialize(this)
@@ -0,0 +1,57 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tntleague.util
import de.steamwar.scoreboard.ScoreboardCallback
import de.steamwar.tntleague.config.targetedBlocks
import de.steamwar.tntleague.game.TNTLeagueGame
import net.kyori.adventure.text.Component
import org.bukkit.entity.Player
import kotlin.collections.HashMap
private val scoreboardTitle by lazy { tntLeaguePrefix.toLegacy() }
data class TNTLeagueScoreboard(val p: Player): ScoreboardCallback {
override fun getData(): HashMap<String, Int> {
val lines = mutableListOf<Component>()
lines.add(Component.space().green())
val minutes = TNTLeagueGame.gameTimeRemaining.floorDiv(60)
val seconds = TNTLeagueGame.gameTimeRemaining.rem(60).toString().padStart(2, '0')
lines.add(translate("scoreboardTime", minutes.toString().yellow(), seconds.yellow()).gray())
lines.add(Component.space().yellow())
with(TNTLeagueGame.blueTeam) {
lines.add(translate("scoreboardTeam", translate(name).colorByTeam(this), (targetedBlocks - damagedBlocks).toString().yellow()).gray())
}
with(TNTLeagueGame.redTeam) {
lines.add(translate("scoreboardTeam", translate(name).colorByTeam(this), (targetedBlocks - damagedBlocks).toString().yellow()).gray())
}
lines.add(Component.space().gray())
return lines
.foldIndexed(HashMap()) { index, acc, component -> acc.also { it[component.translate(p).toLegacy()] = index } }
}
override fun getTitle(): String = scoreboardTitle
}
+10
View File
@@ -0,0 +1,10 @@
name: TNTLeague
version: '1.0.0'
main: de.steamwar.tntleague.TNTLeague
load: POSTWORLD
api-version: '1.21'
dependencies:
- name: SpigotCore
required: true
- name: KotlinCore
required: true
@@ -292,7 +292,7 @@ FIGHT_UNKNOWN_GAMEMODE=§cUnknown gamemode: {0}
FIGHT_UNKNOWN_ARENA=§cThe desired arena does not exist.
FIGHT_IN_ARENA=§cYou are already in an arena.
FIGHT_BROADCAST=§7Click §ehere§7 to fight §e{0} §7against §e{1}!
FIGHT_BROADCAST_HOVER=§aFight §eagainst §7{1}
FIGHT_BROADCAST_HOVER=§aFight §eagainst §7{0}
#CheckCommand
CHECK_REMINDER=§7There are §e{0} §7schematics left for review§8!
@@ -275,7 +275,7 @@ FIGHT_UNKNOWN_GAMEMODE=§cUnbekannter Spielmodus: {0}
FIGHT_UNKNOWN_ARENA=§cDie gewünschte Arena gibt es nicht.
FIGHT_IN_ARENA=§cDu befindest dich bereits in einer Arena.
FIGHT_BROADCAST=§7Klicke §ehier§7, um §e{0} §7gegen §e{1} §7zu §7kämpfen!
FIGHT_BROADCAST_HOVER=§aGegen §7{1} §ekämpfen
FIGHT_BROADCAST_HOVER=§aGegen §7{0} §ekämpfen
#CheckCommand
CHECK_REMINDER=§7Es sind §e{0} §7Schematics zu prüfen§8!
@@ -642,7 +642,6 @@ RANK_PLAYER_FOUND=§eRang §7von §e{0}
RANK_HEADER={0} §e{1} {2}
RANK_UNPLACED=§7unplatziert
RANK_PLACED=§e{0}§8. §7mit §e{1} §7Elo§8.
RANK_EMBLEM=§7Emblem§8: {0}
#Fabric Mod Sender
MODIFICATION_BAN_MESSAGE=Du hast probiert den FabricModSender zu umgehen / zu modifizieren!
@@ -20,6 +20,7 @@
package de.steamwar.velocitycore;
import de.steamwar.messages.Chatter;
import de.steamwar.messages.Message;
import de.steamwar.persistent.Subserver;
import de.steamwar.sql.EventFight;
import de.steamwar.sql.Team;
@@ -43,7 +44,7 @@ public class EventStarter {
public EventStarter() {
EventFight.loadAllComingFights();
VelocityCore.schedule(this::run).delay(10, TimeUnit.SECONDS).schedule();
VelocityCore.schedule(this::run).repeat(10, TimeUnit.SECONDS).schedule();
}
public static Map<Integer, Subserver> getEventServer() {
@@ -73,7 +74,7 @@ public class EventStarter {
} else {
command = "/" + spectatePorts.get(next.getSpectatePort());
}
Chatter.broadcast().system("EVENT_FIGHT_BROADCAST", "EVENT_FIGHT_BROADCAST_HOVER", ClickEvent.runCommand(command), blue.getTeamColor(), blue.getTeamName(), red.getTeamColor(), red.getTeamName());
Chatter.broadcast().system("EVENT_FIGHT_BROADCAST", new Message("EVENT_FIGHT_BROADCAST_HOVER"), ClickEvent.runCommand(command), blue.getTeamColor(), blue.getTeamName(), red.getTeamColor(), red.getTeamName());
}
}
@@ -130,7 +130,7 @@ public class FightCommand extends SWCommand {
public void fight(@Validator("arenaPlayer") PlayerChatter sender, @Mapper("nonHistoricArenaMode") @OptionalValue("") @AllowNull ArenaMode arenaMode, @Mapper("arenaMap") @OptionalValue("") @AllowNull String map) {
createArena(sender, "/fight ", true, arenaMode, map, false,
(p, mode, m) -> new ServerStarter().arena(mode, m).blueLeader(p.getPlayer()).callback(
arena -> Chatter.broadcast().system("FIGHT_BROADCAST", new Message("FIGHT_BROADCAST_HOVER"), ClickEvent.runCommand("/arena " + arena.getServer().getName()), mode.getGameName(), p.getPlayer().getUsername())
arena -> Chatter.broadcast().system("FIGHT_BROADCAST", new Message("FIGHT_BROADCAST_HOVER", p.getPlayer().getUsername()), ClickEvent.runCommand("/arena " + arena.getServer().getName()), mode.getGameName(), p.getPlayer().getUsername())
).start()
);
}
@@ -85,7 +85,7 @@ public class ChannelListener extends ListenerAdapter {
if(permission != null && !sender.user().perms().contains(permission))
return;
command.execute(sender, args.split(" "));
command.execute(sender, args.isEmpty() ? new String[0] : args.split(" "));
});
}
}
@@ -22,9 +22,12 @@ package de.steamwar.velocitycore.listeners;
import com.lunarclient.apollo.ApolloManager;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent;
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
@@ -373,11 +376,11 @@ public class PluginMessage extends BasicListener {
))
register(channel, false, directional(UNKNOWN, DROP));
register("REGISTER", false, directional(this::serverRegistersChannel, this::clientRegistersChannel));
register("minecraft:register", false, directional(this::serverRegistersChannel, this::clientRegistersChannel));
register("REGISTER", false, directional(this::serverRegistersChannel, UNKNOWN));
register("minecraft:register", false, directional(this::serverRegistersChannel, UNKNOWN));
register("MC|Brand", false, directional(this::steamWarBrand, this::userBrand));
register("minecraft:brand", false, directional(this::steamWarBrand, this::userBrand));
register("MC|Brand", false, directional(this::steamWarBrand, UNKNOWN));
register("minecraft:brand", false, directional(this::steamWarBrand, UNKNOWN));
//Needs to be registered cause paper refuses to send PluginMessages on unregistered channels...
register("sw:bridge", true, directional(onlySWSource(async(event -> NetworkPacket.handle(new ServerMetaInfo((ServerConnection) event.getSource()), event.getData()))), UNKNOWN));
@@ -401,6 +404,15 @@ public class PluginMessage extends BasicListener {
// Hackclientlike modsuppressor for labymod: https://github.com/Neocraftr/LabyMod-NeoEssentials (Potentially recognizable from NO Addons/NO Mods?) https://github.com/Neocraftr/LabyMod-NeoEssentials/blob/master/src/main/java/de/neocraftr/neoessentials/utils/BytecodeMethods.java
}
@Subscribe
public void onClientChannelRegister(PlayerChannelRegisterEvent event) {
Player player = event.getPlayer();
for(ChannelIdentifier channel : event.getChannels()) {
channelRegisterHandlers.getOrDefault(channel.getId(), p -> VelocityCore.getLogger().log(Level.WARNING, () -> p.getUsername() + " registered unknown channel " + channel)).accept(player);
}
}
@Subscribe
public void onPluginMessage(PluginMessageEvent event) {
event.setResult(PluginMessageEvent.ForwardResult.handled());
@@ -412,6 +424,20 @@ public class PluginMessage extends BasicListener {
}
}
@Subscribe
public void onClientBrand(PlayerClientBrandEvent event) {
Player player = event.getPlayer();
String brand = event.getBrand();
boolean lunarclient = brand.startsWith("lunarclient:");
VelocityCore.getLogger().log(knownBrands.contains(brand) || lunarclient ? Level.INFO : Level.WARNING, () -> player.getUsername() + " joins with brand: " + brand);
if(lunarclient)
lunar.sendRestrictions(player);
if(brand.equals("badlion"))
badlion.sendRestrictions(player);
}
private void registerPassthroughToClient(String... channels) {
for(String channel : channels) {
channelRegisterHandlers.put(channel, player -> {});
@@ -437,16 +463,6 @@ public class PluginMessage extends BasicListener {
VelocityCore.getProxy().getChannelRegistrar().register(channel.indexOf(':') != -1 ? MinecraftChannelIdentifier.from(channel) : new LegacyChannelIdentifier(channel));
}
private void clientRegistersChannel(PluginMessageEvent event) {
Player player = (Player) event.getSource();
for(String channel : new String(event.getData()).split("\0")) {
channelRegisterHandlers.getOrDefault(channel, p -> VelocityCore.getLogger().log(Level.WARNING, () -> p.getUsername() + " registered unknown channel " + channel)).accept(player);
}
PASS_THROUGH.handle(event);
}
private void serverRegistersChannel(PluginMessageEvent event) {
Player player = (Player) event.getTarget();
@@ -455,22 +471,6 @@ public class PluginMessage extends BasicListener {
send(player, "REGISTER", "minecraft:register", String.join("\0", channels).getBytes());
}
private void userBrand(PluginMessageEvent event) {
Player player = (Player) event.getSource();
ByteBuf buf = Unpooled.wrappedBuffer(event.getData());
String brand = ProtocolUtils.readString(buf);
boolean lunarclient = brand.startsWith("lunarclient:");
VelocityCore.getLogger().log(knownBrands.contains(brand) || lunarclient ? Level.INFO : Level.WARNING, () -> player.getUsername() + " joins with brand: " + brand);
if(lunarclient)
lunar.sendRestrictions(player);
if(brand.equals("badlion"))
badlion.sendRestrictions(player);
PASS_THROUGH.handle(event);
}
private void steamWarBrand(PluginMessageEvent event) {
Player player = (Player) event.getTarget();
String brand = Chatter.of(player).parseToLegacy("STEAMWAR_BRAND", "Velocity", player.getCurrentServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse(""), new String(event.getData(), 1, event.getData().length - 1));
@@ -28,25 +28,25 @@ public class Badlion {
private final byte[] packet;
public Badlion() { //TODO check if working or (json) modsDisallowed wrapper necessary
public Badlion() {
JsonObject disabled = new JsonObject();
disabled.addProperty("disabled", true);
JsonObject json = new JsonObject();
json.add("Clear Glass", disabled);
json.add("ClearWater", disabled);
json.add("FOV Changer", disabled);
json.add("Hitboxes", disabled);
json.add("LevelHead", disabled);
json.add("MiniMap", disabled);
json.add("MLG Cobweb", disabled);
json.add("Replay", disabled); //TODO check if ReplayMod restrictions work
json.add("Schematica", disabled);
json.add("ToggleSneak", disabled);
json.add("ToggleSprint", disabled);
json.add("TNT Time", disabled);
JsonObject modsDisallowed = new JsonObject();
modsDisallowed.add("Clear Glass", disabled);
modsDisallowed.add("ClearWater", disabled);
modsDisallowed.add("FOV Changer", disabled);
modsDisallowed.add("Hitboxes", disabled);
modsDisallowed.add("LevelHead", disabled);
modsDisallowed.add("MiniMap", disabled);
modsDisallowed.add("MLG Cobweb", disabled);
modsDisallowed.add("Replay", disabled);
modsDisallowed.add("Schematica", disabled);
modsDisallowed.add("ToggleSneak", disabled);
modsDisallowed.add("ToggleSprint", disabled);
modsDisallowed.add("TNT Time", disabled);
packet = json.toString().getBytes();
packet = modsDisallowed.toString().getBytes();
}
public void sendRestrictions(Player player) {
@@ -20,13 +20,13 @@
package de.steamwar.velocitycore.network.handlers;
import com.velocitypowered.api.proxy.Player;
import de.steamwar.velocitycore.ArenaMode;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.common.FightEndsPacket;
import de.steamwar.sql.SchematicType;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.UserElo;
import de.steamwar.velocitycore.ArenaMode;
import de.steamwar.velocitycore.VelocityCore;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@@ -52,8 +52,15 @@ public class EloPlayerHandler extends PacketHandler {
*/
@Handler
public void handle(FightEndsPacket fightEndsPacket) {
if (!ArenaMode.getBySchemType(SchematicType.fromDB(fightEndsPacket.getGameMode())).isRanked())
return;
SchematicType schematicType = SchematicType.fromDB(fightEndsPacket.getGameMode());
ArenaMode arenaMode;
if (schematicType == null) {
arenaMode = ArenaMode.getByInternal(fightEndsPacket.getGameMode());
} else {
arenaMode = ArenaMode.getBySchemType(schematicType);
}
if (arenaMode == null) return;
if (!arenaMode.isRanked()) return;
if (EloSchemHandler.publicVsPrivate(fightEndsPacket))
return;
@@ -19,18 +19,21 @@
package de.steamwar.velocitycore.network.handlers;
import de.steamwar.velocitycore.ArenaMode;
import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.common.FightEndsPacket;
import de.steamwar.sql.SchemElo;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SchematicType;
import de.steamwar.velocitycore.ArenaMode;
public class EloSchemHandler extends PacketHandler {
private static final int K = 20;
public static boolean publicVsPrivate(FightEndsPacket packet) {
if (packet.getRedSchem() == -1 && packet.getBlueSchem() == -1) {
return false;
}
SchematicNode blueSchem = SchematicNode.getSchematicNode(packet.getBlueSchem());
SchematicNode redSchem = SchematicNode.getSchematicNode(packet.getRedSchem());
return (blueSchem.getOwner() == 0) != (redSchem.getOwner() == 0);
@@ -38,9 +41,10 @@ public class EloSchemHandler extends PacketHandler {
@Handler
public void handle(FightEndsPacket fightEndsPacket) {
if (!ArenaMode.getBySchemType(SchematicType.fromDB(fightEndsPacket.getGameMode())).isRanked()) {
return;
}
SchematicType type = SchematicType.fromDB(fightEndsPacket.getGameMode());
if (type == null) return;
ArenaMode arenaMode = ArenaMode.getBySchemType(type);
if (!arenaMode.isRanked()) return;
if (publicVsPrivate(fightEndsPacket))
return;
@@ -43,6 +43,7 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.IntStream;
@ChannelHandler.Sharable
@@ -63,7 +64,6 @@ public class Tablist extends ChannelInboundHandlerAdapter {
this.player = player;
this.viewer = Chatter.of(player);
this.directTabItems = Storage.directTabItems.computeIfAbsent(player, p -> new HashMap<>());
injection();
}
public void update(TablistPart global, int seconds) {
@@ -139,7 +139,9 @@ public class Tablist extends ChannelInboundHandlerAdapter {
synchronized (directTabItems) {
directTabItems.clear();
}
}
public void onServerPostSwitch() {
if(player.getProtocolVersion().greaterThan(ProtocolVersion.MINECRAFT_1_20)) {
current.clear();
sendPacket(player, createTeamPacket);
@@ -148,8 +150,14 @@ public class Tablist extends ChannelInboundHandlerAdapter {
private void injection() {
connection = (VelocityServerConnection) player.getCurrentServer().orElse(null);
if(connection == null)
return;
if(connection == null) {
connection = ((ConnectedPlayer) player).getConnectionInFlight();
if(connection == null) {
VelocityCore.getLogger().log(Level.WARNING, "Could not inject Tablist: %s".formatted(player));
return;
}
}
ChannelPipeline pipeline = connection.getConnection().getChannel().pipeline();
if(pipeline.get("steamwar-tablist") != null) //Catch unclean exit
@@ -21,8 +21,9 @@ package de.steamwar.velocitycore.tablist;
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.ServerConnectedEvent;
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import de.steamwar.network.packets.common.FightInfoPacket;
@@ -59,17 +60,19 @@ public class TablistManager extends BasicListener {
}
@Subscribe
public void onJoin(PostLoginEvent event) {
public void onServerConnection(ServerConnectedEvent event) {
synchronized (tablists) {
tablists.put(event.getPlayer(), new Tablist(event.getPlayer()));
tablists.computeIfAbsent(event.getPlayer(), Tablist::new).onServerSwitch();
}
Tablist.sendPacket(event.getPlayer(), Tablist.createTeamPacket);
if(event.getPlayer().getProtocolVersion().noGreaterThan(ProtocolVersion.MINECRAFT_1_20))
Tablist.sendPacket(event.getPlayer(), Tablist.createTeamPacket);
}
@Subscribe
public void onServerConnection(ServerPostConnectEvent event) {
public void onServerPostConnection(ServerPostConnectEvent event) {
synchronized (tablists) {
tablists.get(event.getPlayer()).onServerSwitch();
tablists.get(event.getPlayer()).onServerPostSwitch();
}
}
@@ -90,14 +93,14 @@ public class TablistManager extends BasicListener {
private void updateTablist() {
List<TablistPart> subservers = new ArrayList<>();
for (RegisteredServer server : new ArrayList<>(VelocityCore.getProxy().getAllServers())){
if(server.getPlayersConnected().isEmpty())
for (RegisteredServer server : new ArrayList<>(VelocityCore.getProxy().getAllServers())) {
if (server.getPlayersConnected().isEmpty())
continue;
Subserver subserver = Subserver.getSubserver(server.getServerInfo());
if(fightInfos.containsKey(server))
if (fightInfos.containsKey(server))
subservers.add(new TablistServer(server, fightInfos.get(server)));
else if(subserver == null || subserver.getType() != Servertype.BAUSERVER)
else if (subserver == null || subserver.getType() != Servertype.BAUSERVER)
subservers.add(new TablistServer(server));
}
subservers.add(new TablistBuild());
+8
View File
@@ -27,4 +27,12 @@ sourceSets {
srcDirs("src/")
}
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.10")
}
+56
View File
@@ -0,0 +1,56 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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/>.
*/
plugins {
id "org.jetbrains.kotlin.jvm"
}
kotlin {
jvmToolchain(21)
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
tasks.compileJava {
options.encoding "UTF-8"
}
sourceSets {
main {
java {
srcDirs("src/")
}
resources {
srcDirs("src/")
exclude("**/*.java", "**/*.kt")
}
}
test {
java {
srcDirs("testsrc/")
}
resources {
srcDirs("testsrc/")
exclude("**/*.java", "**/*.kt")
}
}
}
+5 -1
View File
@@ -89,6 +89,7 @@ dependencyResolutionManagement {
includeGroup("org.spigotmc")
includeGroup("io.papermc.paper")
includeGroup("com.velocitypowered")
includeGroup("net.md-5")
}
}
mavenCentral()
@@ -113,6 +114,7 @@ dependencyResolutionManagement {
library("spigotapi", "org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
library("spigotannotations", "org.spigotmc:plugin-annotations:1.2.3-SNAPSHOT")
library("paperapi", "io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT")
library("paperapi21", "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
library("authlib", "com.mojang:authlib:1.5.25")
library("datafixer", "com.mojang:datafixerupper:4.0.26")
library("brigadier", "com.mojang:brigadier:1.0.18")
@@ -174,7 +176,7 @@ include(
"FightSystem:FightSystem_Standalone"
)
// include("KotlinCore")
include("KotlinCore")
include("LobbySystem")
@@ -214,3 +216,5 @@ include(
"VelocityCore",
"VelocityCore:Persistent"
)
include("TNTLeague")