Initial commit for DataLock
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package com.alttd.datalock;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Plugin(
|
||||
id = "data-lock",
|
||||
name = "DataLock",
|
||||
version = BuildConstants.VERSION,
|
||||
description = "A proxy plugin that can be utilized to prevent any plugins from editing data that is currently in use elsewhere",
|
||||
url = "https://alttd.com",
|
||||
authors = {"Teriuihi"}
|
||||
)
|
||||
public class DataLock {
|
||||
|
||||
private static DataLock instance;
|
||||
private final ProxyServer server;
|
||||
private final Logger logger;
|
||||
private final Path dataDirectory;
|
||||
|
||||
@Inject
|
||||
public DataLock(ProxyServer proxyServer, Logger proxyLogger, @DataDirectory Path proxydataDirectory) {
|
||||
instance = this;
|
||||
server = proxyServer;
|
||||
logger = proxyLogger;
|
||||
dataDirectory = proxydataDirectory;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
}
|
||||
|
||||
public static DataLock getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static Logger getLogger() {
|
||||
return getInstance().logger;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.alttd.datalock;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class EventListener {
|
||||
|
||||
private final HashMap<ChannelIdentifier, HashSet<Lock>> channelLockMap = new HashMap<>();
|
||||
private final static List<ChannelIdentifier> channelIdentifierList = new ArrayList<>();
|
||||
|
||||
public EventListener(List<ChannelIdentifier> channelIdentifierList)
|
||||
{
|
||||
EventListener.reload(channelIdentifierList);
|
||||
}
|
||||
|
||||
public static void reload(List<ChannelIdentifier> channelIdentifierList)
|
||||
{
|
||||
EventListener.channelIdentifierList.clear();
|
||||
EventListener.channelIdentifierList.addAll(channelIdentifierList);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPluginMessageEvent(PluginMessageEvent event) {
|
||||
ChannelIdentifier identifier = event.getIdentifier();
|
||||
if (!EventListener.channelIdentifierList.contains(identifier))
|
||||
return;
|
||||
|
||||
event.setResult(PluginMessageEvent.ForwardResult.handled());
|
||||
|
||||
if(event.getSource() instanceof Player) {
|
||||
Logger.warn("Received plugin message from a player");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event.getSource() instanceof ServerConnection serverConnection)) {
|
||||
Logger.warn("Received plugin message from something other than a server.");
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<Lock> hashLock = channelLockMap.getOrDefault(identifier, new HashSet<>());
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput(event.getData());
|
||||
String channel;
|
||||
try {
|
||||
channel = in.readUTF();
|
||||
} catch (IllegalStateException e) {
|
||||
Logger.error("Input stream did not contain enough data, please contact %'s developer.",
|
||||
identifier.getId());
|
||||
return;
|
||||
}
|
||||
String data;
|
||||
try {
|
||||
data = in.readUTF();
|
||||
} catch (IllegalStateException e) {
|
||||
Logger.error("Input stream did not contain enough data, please contact %'s developer.",
|
||||
identifier.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channel.toLowerCase()) {
|
||||
case "try-lock" -> tryLock(identifier, hashLock, data, serverConnection);
|
||||
case "check-lock" -> checkLock(identifier, hashLock, data, serverConnection);
|
||||
case "try-unlock" -> tryUnlock(identifier, hashLock, data, serverConnection);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryLock(ChannelIdentifier identifier, HashSet<Lock> lockSet, String data, ServerConnection serverConnection) {
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("try-lock-result");
|
||||
|
||||
Lock lock = new Lock(serverConnection.getServerInfo().hashCode(), data);
|
||||
if (lockSet.contains(lock)) //An entry from this server already exists, so we can say that it's locked
|
||||
{
|
||||
out.writeBoolean(true);
|
||||
serverConnection.sendPluginMessage(identifier, out.toByteArray());
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<Lock> first = lockSet.stream().filter(a -> a.compareTo(lock) == 0).findFirst();
|
||||
if (first.isPresent()) //An entry from another server exists, so we can't lock it
|
||||
{
|
||||
out.writeBoolean(false);
|
||||
serverConnection.sendPluginMessage(identifier, out.toByteArray());
|
||||
return;
|
||||
}
|
||||
|
||||
//Lock the data
|
||||
lockSet.add(lock);
|
||||
channelLockMap.put(identifier, lockSet);
|
||||
|
||||
out.writeBoolean(true);
|
||||
serverConnection.sendPluginMessage(identifier, out.toByteArray());
|
||||
}
|
||||
|
||||
private void checkLock(ChannelIdentifier identifier, HashSet<Lock> lockSet, String data, ServerConnection serverConnection) {
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
Lock lock = new Lock(serverConnection.hashCode(), data);
|
||||
|
||||
out.writeUTF("check-lock-result");
|
||||
if (lockSet.contains(lock)) //We locked this, but we still return true since it's locked
|
||||
out.writeBoolean(true);
|
||||
else if (lockSet.stream().anyMatch(a -> a.compareTo(lock) == 0))
|
||||
out.writeBoolean(true); //There is a lock (not ours, but it's still locked)
|
||||
else
|
||||
out.writeBoolean(false); //The data is not locked
|
||||
|
||||
serverConnection.sendPluginMessage(identifier, out.toByteArray());
|
||||
}
|
||||
|
||||
private void tryUnlock(ChannelIdentifier identifier, HashSet<Lock> lockSet, String data, ServerConnection serverConnection) {
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("try-unlock-result");
|
||||
|
||||
Lock lock = new Lock(serverConnection.getServerInfo().hashCode(), data);
|
||||
if (lockSet.contains(lock)) //Lock is in the list, but it's made by this server, so we can unlock it
|
||||
{
|
||||
out.writeBoolean(true);
|
||||
lockSet.remove(lock);
|
||||
channelLockMap.put(identifier, lockSet);
|
||||
serverConnection.sendPluginMessage(identifier, out.toByteArray());
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<Lock> first = lockSet.stream().filter(a -> a.compareTo(lock) == 0).findFirst();
|
||||
if (first.isEmpty()) //There is no entry with this data, so we can say it's unlocked
|
||||
{
|
||||
out.writeBoolean(true);
|
||||
serverConnection.sendPluginMessage(identifier, out.toByteArray());
|
||||
return;
|
||||
}
|
||||
|
||||
//There is an entry with this data, but it's not owned by this server, so we can't unlock it
|
||||
out.writeBoolean(false);
|
||||
serverConnection.sendPluginMessage(identifier, out.toByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.alttd.datalock;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Lock implements Comparable {
|
||||
|
||||
private final int serverHash;
|
||||
private final String data;
|
||||
|
||||
public Lock(int serverHash, String data) {
|
||||
this.serverHash = serverHash;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Lock other = (Lock) o;
|
||||
return data.equals(other.data) && serverHash == other.serverHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return Objects.hash(serverHash, data);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Object o) {
|
||||
return ((Lock) o).data.compareTo(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.alttd.datalock;
|
||||
|
||||
public class Logger {
|
||||
|
||||
static private final org.slf4j.Logger logger;
|
||||
|
||||
static {
|
||||
logger = DataLock.getLogger();
|
||||
}
|
||||
|
||||
public static void info(String info, String... variables)
|
||||
{
|
||||
for (String variable : variables) {
|
||||
info = info.replaceFirst("%", variable);
|
||||
}
|
||||
logger.info(info);
|
||||
}
|
||||
|
||||
public static void warn(String warning, String... variables)
|
||||
{
|
||||
for (String variable : variables) {
|
||||
warning = warning.replaceFirst("%", variable);
|
||||
}
|
||||
logger.warn(warning);
|
||||
}
|
||||
|
||||
public static void error(String severe, String... variables)
|
||||
{
|
||||
for (String variable : variables) {
|
||||
severe = severe.replaceFirst("%", variable);
|
||||
}
|
||||
logger.error(severe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.alttd.datalock;
|
||||
|
||||
// The constants are replaced before compilation
|
||||
public class BuildConstants {
|
||||
|
||||
public static final String VERSION = "${version}";
|
||||
}
|
||||
Reference in New Issue
Block a user