Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
904ed87619 | ||
|
|
45b2a7a8a9 | ||
|
|
0ef4793df3 | ||
|
|
4aa276a2d0 | ||
|
|
1e2b22f06c | ||
|
|
dbd9f86be9 | ||
|
|
e71ff635e3 | ||
|
|
4d52ef42e4 | ||
|
|
c21d18594d | ||
|
|
9944bb7077 | ||
|
|
7d43b7399f | ||
|
|
5d797065cb | ||
|
|
43c8489574 | ||
|
|
348dbca900 | ||
|
|
395c63a80b | ||
|
|
99afd0ca71 | ||
|
|
2243be4954 | ||
|
|
8a73b0bf9c | ||
|
|
595b7ed75f | ||
|
|
759b7a886b | ||
|
|
eb68085746 | ||
|
|
16b093e4d6 | ||
|
|
a2848e43d0 | ||
|
|
1bfa28af48 | ||
|
|
b745236131 | ||
|
|
4330d41286 | ||
|
|
75228868f8 | ||
|
|
671c6e17a9 | ||
|
|
c3030d56e8 | ||
|
|
9fe4d6b9c3 | ||
|
|
99dbaa703a | ||
|
|
0fa42b44cc | ||
|
|
7d75bce02c | ||
|
|
60923598b9 | ||
|
|
0c6dc99bcc | ||
|
|
f5776dd1cb | ||
|
|
7134c12826 | ||
|
|
8cc93daf70 | ||
|
|
98dfc345e9 | ||
|
|
590b7618f3 | ||
|
|
4cd2ce55fc | ||
|
|
9fe98a92a9 | ||
|
|
c72972c286 | ||
|
|
92a122eaf8 | ||
|
|
67117b38e3 | ||
|
|
9255062aff | ||
|
|
fe45e48447 | ||
|
|
0391e9e4bf | ||
|
|
59fa4857e3 | ||
|
|
4d663737c1 | ||
|
|
8380ffce9a | ||
|
|
b9e648bba8 | ||
|
|
c34551e630 |
No files matched your search
-16
@@ -1,16 +0,0 @@
|
||||
pipeline:
|
||||
build:
|
||||
image: cogniteev/oracle-java
|
||||
commands: ./gradlew build test --no-daemon
|
||||
publish:
|
||||
image: cogniteev/oracle-java
|
||||
commands:
|
||||
- ./gradlew publishversionedPublicationToParallelBlockRepository
|
||||
- ./gradlew publishLatestToParallelBlockRaw
|
||||
environment:
|
||||
- PUBLISH=yes
|
||||
- LATEST_BUILD=yes
|
||||
secrets: [SNAPSHOT_REPO, RELEASE_REPO, REPO_USERNAME, REPO_PASSWORD, RAW_UPLOAD_PATH]
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
+14
-5
@@ -1,9 +1,17 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.+'
|
||||
}
|
||||
}
|
||||
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
allprojects {
|
||||
group 'com.vexsoftware'
|
||||
version '2.4.0'
|
||||
version '2.6.0'
|
||||
}
|
||||
|
||||
subprojects {
|
||||
@@ -12,18 +20,19 @@ subprojects {
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'https://repo.parallelblock.com/repository/maven-public/'
|
||||
url "https://hub.spigotmc.org/nexus/content/repositories/snapshots"
|
||||
}
|
||||
maven {
|
||||
url 'https://hub.spigotmc.org/nexus/content/groups/public/'
|
||||
url "https://oss.sonatype.org/content/repositories/snapshots"
|
||||
}
|
||||
maven {
|
||||
url "https://oss.sonatype.org/content/groups/public/"
|
||||
url "https://repo.spongepowered.org/maven"
|
||||
}
|
||||
maven {
|
||||
url "http://repo.spongepowered.org/maven"
|
||||
url "https://repo.velocitypowered.com/snapshots/"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,14 +21,16 @@ package com.vexsoftware.votifier;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Files;
|
||||
import com.vexsoftware.votifier.forwarding.BukkitPluginMessagingForwardingSink;
|
||||
import com.vexsoftware.votifier.forwarding.ForwardedVoteListener;
|
||||
import com.vexsoftware.votifier.forwarding.ForwardingVoteSink;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardingVoteSink;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.model.VotifierEvent;
|
||||
import com.vexsoftware.votifier.net.VotifierServerBootstrap;
|
||||
import com.vexsoftware.votifier.net.VotifierSession;
|
||||
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAIO;
|
||||
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAKeygen;
|
||||
import com.vexsoftware.votifier.platform.JavaUtilLogger;
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import com.vexsoftware.votifier.platform.VotifierPlugin;
|
||||
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
|
||||
import com.vexsoftware.votifier.util.KeyCreator;
|
||||
@@ -42,10 +44,6 @@ import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.slf4j.ILoggerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.impl.JDK14LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -63,8 +61,6 @@ import java.util.logging.Level;
|
||||
*/
|
||||
public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, VotifierPlugin, ForwardedVoteListener {
|
||||
|
||||
private static final ILoggerFactory FACTORY = new JDK14LoggerFactory();
|
||||
|
||||
/**
|
||||
* The Votifier instance.
|
||||
*/
|
||||
@@ -97,17 +93,17 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
|
||||
|
||||
private ForwardingVoteSink forwardingMethod;
|
||||
private VotifierScheduler scheduler;
|
||||
private Logger pluginLogger;
|
||||
private LoggingAdapter pluginLogger;
|
||||
|
||||
private boolean loadAndBind() {
|
||||
scheduler = new BukkitScheduler(this);
|
||||
pluginLogger = FACTORY.getLogger(getLogger().getName());
|
||||
pluginLogger = new JavaUtilLogger(getLogger());
|
||||
if (!getDataFolder().exists()) {
|
||||
getDataFolder().mkdir();
|
||||
}
|
||||
|
||||
// Handle configuration.
|
||||
File config = new File(getDataFolder() + "/config.yml");
|
||||
File config = new File(getDataFolder(), "config.yml");
|
||||
|
||||
/*
|
||||
* Use IP address from server.properties as a default for
|
||||
@@ -157,7 +153,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
|
||||
}
|
||||
|
||||
YamlConfiguration cfg;
|
||||
File rsaDirectory = new File(getDataFolder() + "/rsa");
|
||||
File rsaDirectory = new File(getDataFolder(), "rsa");
|
||||
|
||||
// Load configuration.
|
||||
cfg = YamlConfiguration.loadConfiguration(config);
|
||||
@@ -180,7 +176,13 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
|
||||
return false;
|
||||
}
|
||||
|
||||
debug = cfg.getBoolean("debug", false);
|
||||
// the quiet flag always runs priority to the debug flag
|
||||
if (cfg.isBoolean("quiet")) {
|
||||
debug = !cfg.getBoolean("quiet");
|
||||
} else {
|
||||
// otherwise, default to being noisy
|
||||
debug = cfg.getBoolean("debug", true);
|
||||
}
|
||||
|
||||
// Load Votifier tokens.
|
||||
ConfigurationSection tokenSection = cfg.getConfigurationSection("tokens");
|
||||
@@ -214,8 +216,9 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
|
||||
// Initialize the receiver.
|
||||
final String host = cfg.getString("host", hostAddr);
|
||||
final int port = cfg.getInt("port", 8192);
|
||||
if (debug)
|
||||
getLogger().info("DEBUG mode enabled!");
|
||||
if (!debug)
|
||||
getLogger().info("QUIET mode enabled!");
|
||||
|
||||
if (port >= 0) {
|
||||
final boolean disablev1 = cfg.getBoolean("disable-v1-protocol");
|
||||
if (disablev1) {
|
||||
@@ -330,7 +333,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getPluginLogger() {
|
||||
public LoggingAdapter getPluginLogger() {
|
||||
return pluginLogger;
|
||||
}
|
||||
|
||||
|
||||
+5
-11
@@ -1,34 +1,31 @@
|
||||
package com.vexsoftware.votifier.forwarding;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.vexsoftware.votifier.NuVotifierBukkit;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.util.GsonInst;
|
||||
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSink;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Created by Joe Hirschfeld on 10/20/2015.
|
||||
*/
|
||||
public class BukkitPluginMessagingForwardingSink implements ForwardingVoteSink, PluginMessageListener {
|
||||
public class BukkitPluginMessagingForwardingSink extends AbstractPluginMessagingForwardingSink implements PluginMessageListener {
|
||||
|
||||
public BukkitPluginMessagingForwardingSink(Plugin p, String channel, ForwardedVoteListener listener) {
|
||||
super(listener);
|
||||
Validate.notNull(channel, "Channel cannot be null.");
|
||||
this.channel = channel;
|
||||
Bukkit.getMessenger().registerIncomingPluginChannel(p, channel, this);
|
||||
this.listener = listener;
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
private final Plugin p;
|
||||
private final String channel;
|
||||
private final ForwardedVoteListener listener;
|
||||
|
||||
@Override
|
||||
public void halt() {
|
||||
@@ -38,10 +35,7 @@ public class BukkitPluginMessagingForwardingSink implements ForwardingVoteSink,
|
||||
@Override
|
||||
public void onPluginMessageReceived(String s, Player player, byte[] bytes) {
|
||||
try {
|
||||
String message = new String(bytes, StandardCharsets.UTF_8);
|
||||
JsonObject jsonObject = GsonInst.gson.fromJson(message, JsonObject.class);
|
||||
Vote v = new Vote(jsonObject);
|
||||
listener.onForward(v);
|
||||
this.handlePluginMessage(bytes);
|
||||
} catch (Exception e) {
|
||||
NuVotifierBukkit.getInstance().getLogger().log(Level.SEVERE, "There was an unknown error when processing a forwarded vote.", e);
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.vexsoftware.votifier.forwarding;
|
||||
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
|
||||
/**
|
||||
* Represents a method to call when a vote is forwarded through the {@link ForwardingVoteSink}.
|
||||
*/
|
||||
public interface ForwardedVoteListener {
|
||||
|
||||
/**
|
||||
* Called whenever a vote is forwarded from a {@link ForwardingVoteSink}
|
||||
*
|
||||
* @param v Vote that was forwarded
|
||||
*/
|
||||
void onForward(Vote v);
|
||||
|
||||
}
|
||||
@@ -5,10 +5,6 @@ host: %ip%
|
||||
# Port to listen for new votes on
|
||||
port: 8192
|
||||
|
||||
# Whether or not to print debug messages. In a production system, this should be set to false.
|
||||
# This is useful when initially setting up NuVotifier to ensure votes are being delivered.
|
||||
debug: false
|
||||
|
||||
# Setting this option to true will disable handling of Protocol v1 packets. While the old protocol is not secure, this
|
||||
# option is currently not recommended as most voting sites only support the old protocol at present. However, if you are
|
||||
# using NuVotifier's proxy forwarding mechanism, enabling this option will increase your server's security.
|
||||
@@ -26,4 +22,4 @@ forwarding:
|
||||
# - pluginMessaging - Sets up plugin messaging
|
||||
method: none
|
||||
pluginMessaging:
|
||||
channel: NuVotifier:votes
|
||||
channel: nuvotifier:votes
|
||||
@@ -2,7 +2,7 @@ name: Votifier
|
||||
main: com.vexsoftware.votifier.NuVotifierBukkit
|
||||
version: @app.version@
|
||||
description: A plugin that gets notified when votes are made for the server on toplists.
|
||||
authors: [ParallelBlock, blakeman8192, Kramer, tuxed]
|
||||
authors: [Ichbinjoe, blakeman8192, Kramer, tuxed]
|
||||
api-version: 1.13
|
||||
|
||||
commands:
|
||||
|
||||
@@ -6,10 +6,13 @@ import com.google.common.io.Files;
|
||||
import com.vexsoftware.votifier.VoteHandler;
|
||||
import com.vexsoftware.votifier.net.VotifierServerBootstrap;
|
||||
import com.vexsoftware.votifier.platform.BackendServer;
|
||||
import com.vexsoftware.votifier.platform.JavaUtilLogger;
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import com.vexsoftware.votifier.platform.ProxyVotifierPlugin;
|
||||
import com.vexsoftware.votifier.bungee.events.VotifierEvent;
|
||||
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardingVoteSource;
|
||||
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.FileVoteCache;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.MemoryVoteCache;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
|
||||
@@ -31,8 +34,6 @@ import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.impl.DirtyTricks;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -77,18 +78,18 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
|
||||
private ForwardingVoteSource forwardingMethod;
|
||||
|
||||
private VotifierScheduler scheduler;
|
||||
private Logger pluginLogger;
|
||||
private LoggingAdapter pluginLogger;
|
||||
|
||||
private void loadAndBind() {
|
||||
scheduler = new BungeeScheduler(this);
|
||||
pluginLogger = DirtyTricks.getLogger(getLogger());
|
||||
pluginLogger = new JavaUtilLogger(getLogger());
|
||||
if (!getDataFolder().exists()) {
|
||||
getDataFolder().mkdir();
|
||||
}
|
||||
|
||||
// Handle configuration.
|
||||
File config = new File(getDataFolder() + "/config.yml");
|
||||
File rsaDirectory = new File(getDataFolder() + "/rsa");
|
||||
File config = new File(getDataFolder() , "config.yml");
|
||||
File rsaDirectory = new File(getDataFolder() , "rsa");
|
||||
Configuration configuration;
|
||||
|
||||
if (!config.exists()) {
|
||||
@@ -178,9 +179,14 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
|
||||
// Initialize the receiver.
|
||||
final String host = configuration.getString("host", "0.0.0.0");
|
||||
final int port = configuration.getInt("port", 8192);
|
||||
debug = configuration.getBoolean("debug", false);
|
||||
if (debug)
|
||||
getLogger().info("DEBUG mode enabled!");
|
||||
|
||||
if (configuration.get("quiet") != null)
|
||||
debug = !configuration.getBoolean("quiet");
|
||||
else
|
||||
debug = configuration.getBoolean("debug", true);
|
||||
|
||||
if (!debug)
|
||||
getLogger().info("QUIET mode enabled!");
|
||||
|
||||
final boolean disablev1 = configuration.getBoolean("disable-v1-protocol");
|
||||
if (disablev1) {
|
||||
@@ -213,7 +219,9 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
|
||||
if ("none".equals(cacheMethod)) {
|
||||
getLogger().info("Vote cache none selected for caching: votes that cannot be immediately delivered will be lost.");
|
||||
} else if ("memory".equals(cacheMethod)) {
|
||||
voteCache = new MemoryVoteCache(ProxyServer.getInstance().getServers().size());
|
||||
voteCache = new MemoryVoteCache(
|
||||
ProxyServer.getInstance().getServers().size(), this,
|
||||
fwdCfg.getInt("pluginMessaging.memory.cacheTime", -1));
|
||||
getLogger().info("Using in-memory cache for votes that are not able to be delivered.");
|
||||
} else if ("file".equals(cacheMethod)) {
|
||||
try {
|
||||
@@ -225,12 +233,17 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
|
||||
getLogger().log(Level.SEVERE, "Unload to load file cache. Votes will be lost!", e);
|
||||
}
|
||||
}
|
||||
|
||||
int dumpRate = fwdCfg.getInt("pluginMessaging.dumpRate", 5);
|
||||
|
||||
ServerFilter filter = new ServerFilter(
|
||||
fwdCfg.getStringList("pluginMessaging.excludedServers"),
|
||||
fwdCfg.getBoolean("pluginMessaging.whitelist", false)
|
||||
);
|
||||
|
||||
if (!fwdCfg.getBoolean("pluginMessaging.onlySendToJoinedServer")) {
|
||||
|
||||
List<String> ignoredServers = fwdCfg.getStringList("pluginMessaging.excludedServers");
|
||||
|
||||
try {
|
||||
forwardingMethod = new PluginMessagingForwardingSource(channel, ignoredServers, this, voteCache);
|
||||
forwardingMethod = new PluginMessagingForwardingSource(channel, filter, this, voteCache, dumpRate);
|
||||
getLogger().info("Forwarding votes over PluginMessaging channel '" + channel + "' for vote forwarding!");
|
||||
} catch (RuntimeException e) {
|
||||
getLogger().log(Level.SEVERE, "NuVotifier could not set up PluginMessaging for vote forwarding!", e);
|
||||
@@ -239,7 +252,7 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
|
||||
try {
|
||||
String fallbackServer = fwdCfg.getString("pluginMessaging.joinedServerFallback", null);
|
||||
if (fallbackServer != null && fallbackServer.isEmpty()) fallbackServer = null;
|
||||
forwardingMethod = new OnlineForwardPluginMessagingForwardingSource(channel, this, voteCache, fallbackServer);
|
||||
forwardingMethod = new OnlineForwardPluginMessagingForwardingSource(channel, this, filter, voteCache, fallbackServer, dumpRate);
|
||||
} catch (RuntimeException e) {
|
||||
getLogger().log(Level.SEVERE, "NuVotifier could not set up PluginMessaging for vote forwarding!", e);
|
||||
}
|
||||
@@ -378,7 +391,7 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getPluginLogger() {
|
||||
public LoggingAdapter getPluginLogger() {
|
||||
return pluginLogger;
|
||||
}
|
||||
|
||||
|
||||
+7
-4
@@ -1,9 +1,10 @@
|
||||
package com.vexsoftware.votifier.bungee;
|
||||
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.platform.BackendServer;
|
||||
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSource;
|
||||
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
@@ -17,8 +18,9 @@ import net.md_5.bungee.event.EventHandler;
|
||||
* @date 12/31/2015
|
||||
*/
|
||||
public final class OnlineForwardPluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
|
||||
public OnlineForwardPluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache cache, String fallbackServer) {
|
||||
super(channel, nuVotifier, cache);
|
||||
public OnlineForwardPluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, ServerFilter serverFilter,
|
||||
VoteCache cache, String fallbackServer, int dumpRate) {
|
||||
super(channel, serverFilter, nuVotifier, cache, dumpRate);
|
||||
this.fallbackServer = fallbackServer;
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
|
||||
}
|
||||
@@ -28,7 +30,8 @@ public final class OnlineForwardPluginMessagingForwardingSource extends Abstract
|
||||
@Override
|
||||
public void forward(Vote v) {
|
||||
ProxiedPlayer p = ProxyServer.getInstance().getPlayer(v.getUsername());
|
||||
if (p != null && p.getServer() != null) {
|
||||
if (p != null && p.getServer() != null &&
|
||||
serverFilter.isAllowed(p.getServer().getInfo().getName())) {
|
||||
if (forwardSpecific(new BungeeBackendServer(p.getServer().getInfo()), v)) {
|
||||
return;
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,6 +1,7 @@
|
||||
package com.vexsoftware.votifier.bungee;
|
||||
|
||||
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSource;
|
||||
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
@@ -8,17 +9,15 @@ import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
|
||||
|
||||
public PluginMessagingForwardingSource(String channel, List<String> ignoredServers, NuVotifier nuVotifier, VoteCache cache) {
|
||||
super(channel, ignoredServers, nuVotifier, cache);
|
||||
public PluginMessagingForwardingSource(String channel, ServerFilter serverFilter, NuVotifier nuVotifier, VoteCache cache, int dumpRate) {
|
||||
super(channel, serverFilter, nuVotifier, cache, dumpRate);
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
|
||||
}
|
||||
|
||||
protected PluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache) {
|
||||
super(channel, nuVotifier, voteCache);
|
||||
protected PluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache, int dumpRate) {
|
||||
super(channel, nuVotifier, voteCache, dumpRate);
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.slf4j.impl;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class DirtyTricks {
|
||||
private DirtyTricks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static Logger getLogger(java.util.logging.Logger source) {
|
||||
return new JDK14LoggerAdapter(source);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: NuVotifier
|
||||
main: com.vexsoftware.votifier.bungee.NuVotifier
|
||||
version: @app.version@
|
||||
author: ParallelBlock, blakeman8192, Kramer, tuxed
|
||||
author: Ichbinjoe, blakeman8192, Kramer, tuxed
|
||||
@@ -5,10 +5,6 @@ host: 0.0.0.0
|
||||
# Port to listen for new votes on
|
||||
port: 8192
|
||||
|
||||
# Whether or not to print debug messages. In a production system, this should be set to false.
|
||||
# This is useful when initially setting up NuVotifier to ensure votes are being delivered.
|
||||
debug: false
|
||||
|
||||
# All tokens, labeled by the serviceName of each server list.
|
||||
tokens:
|
||||
# Default token for all server lists, if another isn't supplied.
|
||||
@@ -26,21 +22,22 @@ forwarding:
|
||||
# - proxy - Proxies votes to other NuVotifier servers from this server.
|
||||
method: none
|
||||
pluginMessaging:
|
||||
channel: NuVotifier:votes
|
||||
channel: nuvotifier:votes
|
||||
|
||||
#Only send votes to certain servers. If a server is present on excludedServers, then votes will NOT be forwarded
|
||||
#to them. If excludedServers is not present, votes will be sent to all servers.
|
||||
#If onlySendToJoinedServer is true, this option is ignored.
|
||||
|
||||
# If onlySendToJoinedServer is true and a player is on one of these servers, NuVotifier will process the vote as if the
|
||||
# player was not on the network at all.
|
||||
#excludedServers:
|
||||
#- server1
|
||||
#- server2
|
||||
|
||||
# If uncommented, turns excludedServers into an includedServers, where only servers on the list will be used.
|
||||
#whitelist: true
|
||||
|
||||
# Sets which cache to use if there are no players on the server that the vote is being sent to. Supported caches:
|
||||
# - none - Votes will simply be lost when no players are present on the server.
|
||||
# - memory - Votes will be cached in memory when no players are present. When Bungee is restarted, the votes will be lost.
|
||||
# This method is NOT recommended for production systems!
|
||||
# - file - Votes will be saved to a local file every 3 minutes and on shutdown and reloaded when Bungee is restarted. This is suitable for
|
||||
# most set ups.
|
||||
cache: file
|
||||
@@ -50,11 +47,17 @@ forwarding:
|
||||
#If you do not want the vote forwarded to a fallback, set this value to empty ('')
|
||||
#ONLY USED IF onlySendToJoinedServer is true!!
|
||||
joinedServerFallback: 'Hub'
|
||||
# Defines how quickly to dump votes over a player's connection when offloading a cache in votes per second
|
||||
dumpRate: 5
|
||||
# Options for file caching.
|
||||
file:
|
||||
name: cached-votes.json
|
||||
# days before a vote is considered 'dead' - removed from cache with a console warning
|
||||
voteTTL: 10
|
||||
cacheTime: 10
|
||||
# Options for memory caching.
|
||||
memory:
|
||||
# days before a vote is considered 'dead' and removed from memory. All votes are removed when the server restarts. -1 signifies no TTL
|
||||
cacheTime: -1
|
||||
# Specify servers to proxy votes for.
|
||||
proxy:
|
||||
Hub:
|
||||
|
||||
+1
-3
@@ -1,9 +1,7 @@
|
||||
dependencies {
|
||||
compile group: "io.netty", name: "netty-handler", version: "4.1.16.Final"
|
||||
compile group: "io.netty", name: "netty-handler", version: "4.1.32.Final"
|
||||
compile 'com.google.code.gson:gson:2.8.0'
|
||||
compile 'com.google.guava:guava:24.1-jre'
|
||||
compile 'org.slf4j:slf4j-api:1.7.25'
|
||||
compile 'org.slf4j:slf4j-jdk14:1.7.25'
|
||||
testCompile group: "junit", name: "junit", version: "4.12"
|
||||
testCompile group: "org.json", name: "json", version: "20180130" // retain this for testing reasons
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public class VotifierServerBootstrap {
|
||||
if (socketAddress == null) {
|
||||
socketAddress = new InetSocketAddress(host, port);
|
||||
}
|
||||
plugin.getPluginLogger().error("Votifier was not able to bind to {}", socketAddress, future.cause());
|
||||
plugin.getPluginLogger().error("Votifier was not able to bind to " + socketAddress.toString(), future.cause());
|
||||
error.accept(future.cause());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -39,7 +39,6 @@ public class VoteInboundHandler extends SimpleChannelInboundHandler<Vote> {
|
||||
handler.onError(ctx.channel(), session.hasCompletedVote(), cause);
|
||||
|
||||
if (session.getVersion() == VotifierSession.ProtocolVersion.TWO) {
|
||||
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty("status", "error");
|
||||
object.addProperty("cause", cause.getClass().getSimpleName());
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package com.vexsoftware.votifier.net.protocol.v1crypto;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -59,6 +60,19 @@ public class RSAIO {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readB64File(File directory, String name) throws IOException {
|
||||
File f = new File(directory, name);
|
||||
byte[] contents = Files.readAllBytes(f.toPath());
|
||||
String strContents = new String(contents);
|
||||
strContents = strContents.trim();
|
||||
try {
|
||||
return Base64.getDecoder().decode(strContents);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("Base64 decoding exception: This is probably due to a corrupted file, " +
|
||||
"but in case it isn't, here is a b64 representation of what we read: " + new String(Base64.getEncoder().encode(contents), StandardCharsets.UTF_8), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an RSA key pair from a directory. The directory must have the files
|
||||
* "public.key" and "private.key".
|
||||
@@ -69,14 +83,10 @@ public class RSAIO {
|
||||
*/
|
||||
public static KeyPair load(File directory) throws Exception {
|
||||
// Read the public key file.
|
||||
File publicKeyFile = new File(directory + "/public.key");
|
||||
byte[] encodedPublicKey = Files.readAllBytes(publicKeyFile.toPath());
|
||||
encodedPublicKey = Base64.getDecoder().decode(new String(encodedPublicKey, StandardCharsets.UTF_8));
|
||||
byte[] encodedPublicKey = readB64File(directory, "public.key");
|
||||
|
||||
// Read the private key file.
|
||||
File privateKeyFile = new File(directory + "/private.key");
|
||||
byte[] encodedPrivateKey = Files.readAllBytes(privateKeyFile.toPath());
|
||||
encodedPrivateKey = Base64.getDecoder().decode(new String(encodedPrivateKey, StandardCharsets.UTF_8));
|
||||
byte[] encodedPrivateKey = readB64File(directory, "private.key");
|
||||
|
||||
// Instantiate and return the key pair.
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.vexsoftware.votifier.platform;
|
||||
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class JavaUtilLogger implements LoggingAdapter {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public JavaUtilLogger(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String s) {
|
||||
logger.severe(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, Object... o) {
|
||||
logger.log(Level.SEVERE, s, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String s) {
|
||||
logger.warning(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String s, Object... o) {
|
||||
logger.log(Level.WARNING, s, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String s) {
|
||||
logger.info(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String s, Object... o) {
|
||||
logger.log(Level.INFO, s, o);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.vexsoftware.votifier.platform;
|
||||
|
||||
public interface LoggingAdapter {
|
||||
void error(String s);
|
||||
void error(String s, Object... o);
|
||||
|
||||
void warn(String s);
|
||||
void warn(String s, Object... o);
|
||||
|
||||
void info(String s);
|
||||
void info(String s, Object... o);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.vexsoftware.votifier.platform;
|
||||
import com.vexsoftware.votifier.VoteHandler;
|
||||
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
@@ -16,7 +15,7 @@ public interface VotifierPlugin extends VoteHandler {
|
||||
|
||||
KeyPair getProtocolV1Key();
|
||||
|
||||
Logger getPluginLogger();
|
||||
LoggingAdapter getPluginLogger();
|
||||
|
||||
VotifierScheduler getScheduler();
|
||||
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.vexsoftware.votifier.support.forwarding;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
|
||||
import java.io.CharArrayReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public abstract class AbstractPluginMessagingForwardingSink implements ForwardingVoteSink {
|
||||
|
||||
public AbstractPluginMessagingForwardingSink(ForwardedVoteListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private final ForwardedVoteListener listener;
|
||||
|
||||
public void handlePluginMessage(byte[] message) {
|
||||
String strMessage = new String(message, StandardCharsets.UTF_8);
|
||||
try (CharArrayReader reader = new CharArrayReader(strMessage.toCharArray())) {
|
||||
JsonReader r = new JsonReader(reader);
|
||||
r.setLenient(true);
|
||||
while (r.peek() != JsonToken.END_DOCUMENT) {
|
||||
r.beginObject();
|
||||
JsonObject o = new JsonObject();
|
||||
|
||||
while (r.hasNext()) {
|
||||
String name = r.nextName();
|
||||
if (r.peek() == JsonToken.NUMBER)
|
||||
o.add(name, new JsonPrimitive(r.nextLong()));
|
||||
else
|
||||
o.add(name, new JsonPrimitive(r.nextString()));
|
||||
}
|
||||
r.endObject();
|
||||
|
||||
Vote v = new Vote(o);
|
||||
listener.onForward(v);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(); // Should never happen.
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
-30
@@ -1,51 +1,60 @@
|
||||
package com.vexsoftware.votifier.support.forwarding;
|
||||
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.platform.BackendServer;
|
||||
import com.vexsoftware.votifier.platform.ProxyVotifierPlugin;
|
||||
import com.vexsoftware.votifier.platform.VotifierPlugin;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.FileVoteCache;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class AbstractPluginMessagingForwardingSource implements ForwardingVoteSource {
|
||||
|
||||
public AbstractPluginMessagingForwardingSource(String channel, List<String> ignoredServers, ProxyVotifierPlugin plugin, VoteCache cache) {
|
||||
public AbstractPluginMessagingForwardingSource(String channel, ServerFilter serverFilter, ProxyVotifierPlugin plugin, VoteCache cache, int dumpRate) {
|
||||
this.channel = channel;
|
||||
this.plugin = plugin;
|
||||
this.cache = cache;
|
||||
this.ignoredServers = ignoredServers;
|
||||
this.serverFilter = serverFilter;
|
||||
this.dumpRate = dumpRate;
|
||||
}
|
||||
|
||||
protected AbstractPluginMessagingForwardingSource(String channel, ProxyVotifierPlugin plugin, VoteCache voteCache) {
|
||||
this(channel, null, plugin, voteCache);
|
||||
protected AbstractPluginMessagingForwardingSource(String channel, ProxyVotifierPlugin plugin, VoteCache voteCache, int dumpRate) {
|
||||
this(channel, null, plugin, voteCache, dumpRate);
|
||||
}
|
||||
|
||||
protected final ProxyVotifierPlugin plugin;
|
||||
protected final String channel;
|
||||
protected final VoteCache cache;
|
||||
protected final List<String> ignoredServers;
|
||||
protected final ServerFilter serverFilter;
|
||||
private final int dumpRate;
|
||||
|
||||
@Override
|
||||
public void forward(Vote v) {
|
||||
byte[] rawData = v.serialize().toString().getBytes(StandardCharsets.UTF_8);
|
||||
for (BackendServer server : plugin.getAllBackendServers()) {
|
||||
if (ignoredServers != null && ignoredServers.contains(server.getName())) continue;
|
||||
if (!serverFilter.isAllowed(server.getName())) continue;
|
||||
if (!forwardSpecific(server, rawData)) attemptToAddToCache(v, server.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean forwardSpecific(BackendServer connection, Vote vote) {
|
||||
byte[] rawData = vote.serialize().toString().getBytes(StandardCharsets.UTF_8);
|
||||
return connection.sendPluginMessage(channel, rawData);
|
||||
return forwardSpecific(connection, rawData);
|
||||
}
|
||||
|
||||
protected boolean forwardSpecific(BackendServer connection, Collection<Vote> votes) {
|
||||
StringBuilder data = new StringBuilder();
|
||||
for (Vote v : votes) {
|
||||
data.append(v.serialize().toString());
|
||||
}
|
||||
|
||||
return forwardSpecific(connection, data.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private boolean forwardSpecific(BackendServer connection, byte[] data) {
|
||||
@@ -92,38 +101,51 @@ public abstract class AbstractPluginMessagingForwardingSource implements Forward
|
||||
}
|
||||
|
||||
// returns a collection of failed votes
|
||||
private void dumpVotesToServer(Collection<Vote> cachedVotes, BackendServer target, String identifier, Consumer<List<Vote>> cb) {
|
||||
List<Vote> failures = new ArrayList<>();
|
||||
|
||||
private void dumpVotesToServer(Collection<Vote> cachedVotes, BackendServer target, String identifier, Consumer<Collection<Vote>> cb) {
|
||||
dumpVotesToServer(cachedVotes, target, identifier, 0, cb);
|
||||
}
|
||||
private void dumpVotesToServer(Collection<Vote> cachedVotes, BackendServer target, String identifier, int evictedAlready, Consumer<Collection<Vote>> cb) {
|
||||
if (!cachedVotes.isEmpty()) {
|
||||
plugin.getScheduler().delayedOnPool(() -> {
|
||||
int evicted = 0;
|
||||
int unsuccessfulEvictions = 0;
|
||||
for (Vote v : cachedVotes) {
|
||||
if (forwardSpecific(target, v)) {
|
||||
evicted++;
|
||||
} else {
|
||||
// Re-add to cache to send later.
|
||||
failures.add(v);
|
||||
unsuccessfulEvictions++;
|
||||
}
|
||||
Iterator<Vote> vi = cachedVotes.iterator();
|
||||
Collection<Vote> chunk = new ArrayList<>(dumpRate);
|
||||
while (vi.hasNext() && evicted < dumpRate) {
|
||||
chunk.add(vi.next());
|
||||
vi.remove();
|
||||
}
|
||||
|
||||
if (forwardSpecific(target, chunk)) {
|
||||
evicted += chunk.size();
|
||||
|
||||
if (evicted >= dumpRate && !cachedVotes.isEmpty()) {
|
||||
// if we evicted everything we could but still need to evict more
|
||||
dumpVotesToServer(cachedVotes, target, identifier, evictedAlready + evicted, cb);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// so since our forwarding failed, break like we are done
|
||||
cachedVotes.addAll(chunk);
|
||||
}
|
||||
|
||||
if (plugin.isDebug()) {
|
||||
plugin.getPluginLogger().info("Successfully evicted " + evicted + " votes to " + identifier + ".");
|
||||
if (unsuccessfulEvictions > 0) {
|
||||
plugin.getPluginLogger().info("Held " + unsuccessfulEvictions + " votes for " + identifier + ".");
|
||||
plugin.getPluginLogger().info("Successfully evicted " + (evictedAlready + evicted) + " votes to " + identifier + ".");
|
||||
if (!cachedVotes.isEmpty()) {
|
||||
plugin.getPluginLogger().info("Held " + cachedVotes.size() + " votes for " + identifier + ".");
|
||||
}
|
||||
}
|
||||
cb.accept(failures);
|
||||
}, 3, TimeUnit.SECONDS);
|
||||
|
||||
// cachedVotes contains any votes which have yet to be evicted
|
||||
cb.accept(cachedVotes);
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
} else {
|
||||
cb.accept(failures);
|
||||
cb.accept(cachedVotes);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handlePlayerSwitch(BackendServer server, String playerName) {
|
||||
if (cache == null) return;
|
||||
if (ignoredServers != null && ignoredServers.contains(server.getName())) return;
|
||||
if (!serverFilter.isAllowed(server.getName())) return;
|
||||
|
||||
final Collection<Vote> cachedVotes = cache.evictPlayer(playerName);
|
||||
dumpVotesToServer(cachedVotes, server, "player '" + playerName + "'", failedVotes -> {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.vexsoftware.votifier.sponge.forwarding;
|
||||
package com.vexsoftware.votifier.support.forwarding;
|
||||
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.vexsoftware.votifier.forwarding;
|
||||
package com.vexsoftware.votifier.support.forwarding;
|
||||
|
||||
/**
|
||||
* Represents a method at which to receive forwarded votes.
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.vexsoftware.votifier.support.forwarding;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public class ServerFilter {
|
||||
|
||||
public ServerFilter(Collection<String> names, boolean whitelist) {
|
||||
if (names == null)
|
||||
names = Collections.EMPTY_SET;
|
||||
|
||||
this.names = names;
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
|
||||
public ServerFilter() {
|
||||
this(Collections.EMPTY_SET, true);
|
||||
}
|
||||
|
||||
private final Collection<String> names;
|
||||
private final boolean whitelist;
|
||||
|
||||
public boolean isAllowed(String name) {
|
||||
return names.contains(name) == whitelist;
|
||||
}
|
||||
}
|
||||
+7
-15
@@ -5,28 +5,25 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import com.vexsoftware.votifier.platform.VotifierPlugin;
|
||||
import com.vexsoftware.votifier.platform.scheduler.ScheduledVotifierTask;
|
||||
import com.vexsoftware.votifier.util.GsonInst;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FileVoteCache extends MemoryVoteCache {
|
||||
|
||||
private final Logger l;
|
||||
private final LoggingAdapter l;
|
||||
private final File cacheFile;
|
||||
private final long voteTTL;
|
||||
private final ScheduledVotifierTask saveTask;
|
||||
|
||||
public FileVoteCache(int initialMemorySize, final VotifierPlugin plugin, File cacheFile, long voteTTL) throws IOException {
|
||||
super(initialMemorySize);
|
||||
super(initialMemorySize, plugin, voteTTL);
|
||||
this.cacheFile = cacheFile;
|
||||
this.voteTTL = voteTTL;
|
||||
this.l = plugin.getPluginLogger();
|
||||
|
||||
load();
|
||||
@@ -76,11 +73,11 @@ public class FileVoteCache extends MemoryVoteCache {
|
||||
JsonObject servers = object.getAsJsonObject("servers");
|
||||
|
||||
for (String player : keySet(players)) {
|
||||
playerVoteCache.put(player, readVotes(players.getAsJsonArray(player)));
|
||||
playerVoteCache.putAll(player, readVotes(players.getAsJsonArray(player)));
|
||||
}
|
||||
|
||||
for (String server : keySet(servers)) {
|
||||
voteCache.put(server, readVotes(servers.getAsJsonArray(server)));
|
||||
voteCache.putAll(server, readVotes(servers.getAsJsonArray(server)));
|
||||
}
|
||||
|
||||
if (resave) {
|
||||
@@ -119,8 +116,8 @@ public class FileVoteCache extends MemoryVoteCache {
|
||||
JsonObject votesObject = new JsonObject();
|
||||
votesObject.addProperty("version", 2);
|
||||
try {
|
||||
votesObject.add("players", serializeMap(playerVoteCache));
|
||||
votesObject.add("servers", serializeMap(voteCache));
|
||||
votesObject.add("players", serializeMap(playerVoteCache.asMap()));
|
||||
votesObject.add("servers", serializeMap(voteCache.asMap()));
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
@@ -159,11 +156,6 @@ public class FileVoteCache extends MemoryVoteCache {
|
||||
return o;
|
||||
}
|
||||
|
||||
private boolean hasTimedOut(Vote v) {
|
||||
if (voteTTL == -1) return false;
|
||||
// scale voteTTL to milliseconds
|
||||
return v.getLocalTimestamp() + voteTTL * 24 * 60 * 60 * 1000 < System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void halt() throws IOException {
|
||||
saveTask.cancel();
|
||||
|
||||
Vendored
+53
-23
@@ -1,9 +1,15 @@
|
||||
package com.vexsoftware.votifier.support.forwarding.cache;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import com.vexsoftware.votifier.platform.VotifierPlugin;
|
||||
import com.vexsoftware.votifier.platform.scheduler.ScheduledVotifierTask;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
@@ -11,17 +17,26 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
public class MemoryVoteCache implements VoteCache {
|
||||
|
||||
public MemoryVoteCache(int initialSize) {
|
||||
voteCache = new HashMap<>(initialSize);
|
||||
this.playerVoteCache = new HashMap<>();
|
||||
}
|
||||
private final LoggingAdapter l;
|
||||
private final long voteTTL;
|
||||
|
||||
protected final Map<String, Collection<Vote>> voteCache;
|
||||
|
||||
protected final Map<String, Collection<Vote>> playerVoteCache;
|
||||
protected final Multimap<String, Vote> voteCache;
|
||||
protected final Multimap<String, Vote> playerVoteCache;
|
||||
|
||||
protected final ReentrantLock cacheLock = new ReentrantLock();
|
||||
|
||||
private final ScheduledVotifierTask sweepTask;
|
||||
|
||||
public MemoryVoteCache(int initialSize, VotifierPlugin p, long voteTTL) {
|
||||
voteCache = HashMultimap.create(initialSize, 10); // 10 is a complete guess. I have no idea .-.
|
||||
playerVoteCache = HashMultimap.create();
|
||||
|
||||
this.voteTTL = voteTTL;
|
||||
|
||||
this.l = p.getPluginLogger();
|
||||
this.sweepTask = p.getScheduler().repeatOnPool(this::sweep, 12, 12, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getCachedServers() {
|
||||
cacheLock.lock();
|
||||
@@ -37,12 +52,7 @@ public class MemoryVoteCache implements VoteCache {
|
||||
if (server == null) throw new NullPointerException();
|
||||
cacheLock.lock();
|
||||
try {
|
||||
Collection<Vote> voteCollection = voteCache.get(server);
|
||||
if (voteCollection == null) {
|
||||
voteCollection = new ArrayList<>();
|
||||
voteCache.put(server, voteCollection);
|
||||
}
|
||||
voteCollection.add(v);
|
||||
voteCache.put(server, v);
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
@@ -53,12 +63,7 @@ public class MemoryVoteCache implements VoteCache {
|
||||
if (player == null) throw new NullPointerException();
|
||||
cacheLock.lock();
|
||||
try {
|
||||
Collection<Vote> voteCollection = playerVoteCache.get(player);
|
||||
if (voteCollection == null) {
|
||||
voteCollection = new ArrayList<>();
|
||||
playerVoteCache.put(player, voteCollection);
|
||||
}
|
||||
voteCollection.add(v);
|
||||
playerVoteCache.put(player, v);
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
@@ -69,8 +74,7 @@ public class MemoryVoteCache implements VoteCache {
|
||||
if (player == null) throw new NullPointerException();
|
||||
cacheLock.lock();
|
||||
try {
|
||||
Collection<Vote> fromCollection = playerVoteCache.remove(player);
|
||||
return fromCollection != null ? fromCollection : ImmutableList.of();
|
||||
return new HashSet<>(playerVoteCache.removeAll(player));
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
@@ -81,10 +85,36 @@ public class MemoryVoteCache implements VoteCache {
|
||||
if (server == null) throw new NullPointerException();
|
||||
cacheLock.lock();
|
||||
try {
|
||||
Collection<Vote> fromCollection = voteCache.remove(server);
|
||||
return fromCollection != null ? fromCollection : ImmutableList.of();
|
||||
return new HashSet<>(voteCache.removeAll(server));
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void sweep() {
|
||||
cacheLock.lock();
|
||||
try {
|
||||
sweep(voteCache);
|
||||
sweep(playerVoteCache);
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void sweep(Multimap<?, Vote> m) {
|
||||
Iterator<Vote> vi = m.values().iterator();
|
||||
while (vi.hasNext()) {
|
||||
Vote v = vi.next();
|
||||
if (hasTimedOut(v)) {
|
||||
l.warn("Purging out of date vote.", v);
|
||||
vi.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasTimedOut(Vote v) {
|
||||
if (voteTTL == -1) return false;
|
||||
// scale voteTTL to milliseconds
|
||||
return v.getLocalTimestamp() + voteTTL * 24 * 60 * 60 * 1000 < System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -1,14 +1,14 @@
|
||||
package com.vexsoftware.votifier.util.standalone;
|
||||
|
||||
import com.vexsoftware.votifier.net.VotifierServerBootstrap;
|
||||
import com.vexsoftware.votifier.platform.JavaUtilLogger;
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import com.vexsoftware.votifier.platform.VotifierPlugin;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.net.VotifierSession;
|
||||
import com.vexsoftware.votifier.platform.scheduler.ScheduledExecutorServiceVotifierScheduler;
|
||||
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
|
||||
import io.netty.channel.Channel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.Key;
|
||||
@@ -18,6 +18,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class StandaloneVotifierPlugin implements VotifierPlugin {
|
||||
private final Map<String, Key> tokens;
|
||||
@@ -58,8 +59,8 @@ public class StandaloneVotifierPlugin implements VotifierPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getPluginLogger() {
|
||||
return LoggerFactory.getLogger(StandaloneVotifierPlugin.class);
|
||||
public LoggingAdapter getPluginLogger() {
|
||||
return new JavaUtilLogger(Logger.getAnonymousLogger());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,11 +2,11 @@ package com.vexsoftware.votifier.net.protocol;
|
||||
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.net.VotifierSession;
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import com.vexsoftware.votifier.platform.VotifierPlugin;
|
||||
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
|
||||
import com.vexsoftware.votifier.util.KeyCreator;
|
||||
import io.netty.channel.Channel;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -76,7 +76,7 @@ public class TestVotifierPlugin implements VotifierPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getPluginLogger() {
|
||||
public LoggingAdapter getPluginLogger() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.vexsoftware.votifier.support.forwarding;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PMForwardingSinkTest {
|
||||
|
||||
@Test
|
||||
public void testSuccessfulMultiDecode() throws Exception {
|
||||
List<Vote> receivedVotes = Lists.newArrayList();
|
||||
ForwardedVoteListener vl = receivedVotes::add;
|
||||
|
||||
List<Vote> sentVotes = Lists.newArrayList(
|
||||
new Vote("serviceA", "usernameA", "1.1.1.1", "1546300800"),
|
||||
new Vote("serviceB", "usernameBBBBBBB", "1.2.23.4", "1514764800", System.currentTimeMillis())
|
||||
);
|
||||
|
||||
StringBuilder message = new StringBuilder();
|
||||
|
||||
for (Vote v : sentVotes) {
|
||||
message.append(v.serialize());
|
||||
}
|
||||
|
||||
byte[] messageBytes = message.toString().getBytes();
|
||||
System.out.println(message.toString());
|
||||
|
||||
AbstractPluginMessagingForwardingSink sink = new AbstractPluginMessagingForwardingSink(vl) {
|
||||
@Override
|
||||
public void halt() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
sink.handlePluginMessage(messageBytes);
|
||||
|
||||
assertEquals(sentVotes.size(), receivedVotes.size());
|
||||
|
||||
for (int i = 0; i < receivedVotes.size(); i++) {
|
||||
assertEquals(sentVotes.get(i), receivedVotes.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
This document outlines various developer resources concerning NuVotifier.
|
||||
|
||||
# Contributing
|
||||
|
||||
NuVotifier is an open source GNU GPLv3 licensed project. We accept contributions
|
||||
through pull requests, and will make sure to credit you for your gracious
|
||||
contribution.
|
||||
|
||||
# Building NuVotifier
|
||||
|
||||
NuVotifier can be built by running the following: `./gradlew build` or
|
||||
`./gradlew.bat build` on Windows. The resultant universal jar is built and
|
||||
written to `universal/build/libs/nuvotifier-{version}.jar`.
|
||||
|
||||
The build directories can be cleaned instead using the `./gradlew clean` (and
|
||||
`./gradlew.bat build`) command.
|
||||
|
||||
# NuVotifier as a plugin dependency
|
||||
|
||||
ParallelBlock now maintains an independent Maven repository for free use by the
|
||||
community. Builds are automatically published by a CI operated by ParallelBlock.
|
||||
|
||||
## Maven
|
||||
|
||||
You can have your project depend on NuVotifier as a dependency through the
|
||||
following code snippets:
|
||||
|
||||
```
|
||||
<project>
|
||||
...
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>parallelblock-public</id>
|
||||
<name>ParallelBlock Public Repository</name>
|
||||
<url>https://repo.parallelblock.com/repository/maven-public/</url>
|
||||
</repository>
|
||||
...
|
||||
</repositories>
|
||||
...
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.vexsoftware</groupId>
|
||||
<artifactId>nuvotifier-universal</artifactId>
|
||||
<version>...</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
...
|
||||
</dependencies>
|
||||
...
|
||||
</project>
|
||||
```
|
||||
|
||||
## Gradle
|
||||
|
||||
You can include NuVotifier into your gradle project using the following lines:
|
||||
|
||||
```
|
||||
...
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://repo.parallelblock.com/repository/maven-public/'
|
||||
}
|
||||
...
|
||||
}
|
||||
...
|
||||
dependencies {
|
||||
compileOnly "com.vexsoftware:nuvotifier-universal:version"
|
||||
...
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
# Implementing NuVotifier v2 protocol in a server list
|
||||
|
||||
NuVotifier provides production ready reference implementations for interacting
|
||||
with NuVotifier servers. These libraries are available below:
|
||||
|
||||
- [NodeJS](https://github.com/NuVotifier/votifier2-js)
|
||||
- [PHP](https://github.com/NuVotifier/votifier2-php)
|
||||
- [Golang](https://github.com/minecrafter/go-votifier)
|
||||
@@ -1,16 +0,0 @@
|
||||
# Documentation
|
||||
|
||||
This directory holds documentation for NuVotifier.
|
||||
|
||||
# Usage Guide
|
||||
|
||||
The usage/configuration guide may be found [here](usage.md).
|
||||
|
||||
# Developer Guide
|
||||
|
||||
The developer guide for NuVotifier may be found [here](developer.md).
|
||||
|
||||
# Technical QA
|
||||
|
||||
Some assorted technical questions not important to most people are found
|
||||
[here](technical_qa.md).
|
||||
@@ -1,78 +0,0 @@
|
||||
This document outlines some of the technical reasons for a new NuVotifier.
|
||||
|
||||
# Why a new protocol?
|
||||
|
||||
The old Votifier protocol has three major flaws, which we feel are corrected in the new protocol.
|
||||
|
||||
* The data is encrypted (with RSA) but not hashed to verify its integrity. The new protocol utilizes HMAC with SHA256 (but no encryption, given that the data sent is eventually revealed in some form or another).
|
||||
* The canonical Votifier protocol is vulnerable to replay attacks.
|
||||
* The canonical Votifier is one key for all clients, which is weak. While NuVotifier still supports a default key (for the less paranoid), this ability can be disabled and force per-server keys.
|
||||
|
||||
# Is NuVotifier a drop-in replacement for Votifier?
|
||||
|
||||
Yes. NuVotifier is compatible with Votifier server lists and Votifier listeners, with some differences:
|
||||
|
||||
* The `Vote` object's setters are deprecated.
|
||||
* Support for standalone vote listeners (`.class` files) has been removed, as handling `VotifierEvent` is more flexible.
|
||||
|
||||
# Protocol Documentation
|
||||
|
||||
This documentation is for server lists that wish to add NuVotifier support.
|
||||
|
||||
There are two versions of the NuVotifier protocol.
|
||||
|
||||
## Handshake
|
||||
|
||||
A connection is made to the NuVotifier server by the server list, and immediately NuVotifier will send its version in the following packet:
|
||||
|
||||
"VOTIFIER <version> <challenge>"
|
||||
|
||||
The challenge will not be present in version 1.x servers.
|
||||
|
||||
## Protocol v2
|
||||
|
||||
NuVotifier expects a message composed of `0x733A` (16-bit big-endian integer) and the length of the following JSON message (as a 16-bit big-endian integer) sent plus the message.
|
||||
|
||||
The message is JSON-encoded containing the following data:
|
||||
|
||||
* `payload` contains the fully encoded vote. It is a JSON message containing the `serviceName` (string), `username` (string), `address` (string, but must be a valid IPv4 or IPv6 address), `timestamp` (a long with the time this vote was taken in _milliseconds_), and `challenge` (from the handshake). It is optional, but recommended to supply a `uuid` field with the player's UUID (with dashes, e.g. `652a2bc4-e8cd-405d-b7b6-98156ee2dc09`).
|
||||
* `signature` is a HMAC-SHA256 digest of the payload with a owner-provided key.
|
||||
|
||||
A response is given after the vote has been received and parsed, but not yet processed.
|
||||
|
||||
For your convenience, the NuVotifier team has provided client libraries in [JavaScript](https://github.com/NuVotifier/votifier2-js), [PHP](https://github.com/NuVotifier/votifier2-php) and [Go](https://github.com/minecrafter/go-votifier). If you are stuck, these libraries may help you.
|
||||
|
||||
## Protocol v1 (deprecated)
|
||||
|
||||
For better compatibility, NuVotifier supports the old Votifier protocol as well.
|
||||
|
||||
NuVotifier expects a 256 byte RSA encrypted block (the public key should be obtained by the NuVotifier user), with the following format:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>string</td>
|
||||
<td>VOTE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>string</td>
|
||||
<td>serviceName</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>string</td>
|
||||
<td>username</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>string</td>
|
||||
<td>address</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>string</td>
|
||||
<td>timeStamp</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
The first string of value "VOTE" is an opcode check to ensure that RSA was encoded and decoded properly, if this value is wrong then NuVotifier assumes that there was a problem with encryption and drops the connection. `serviceName` is the name of the top list service, `username` is the username (entered by the voter) of the person who voted, `address` is the IP address of the voter, and `timeStamp` is the time stamp of the vote. Each string is delimited by the newline character `\n` (byte value 10). The `space` block is the empty space that is left over, **the block must be exactly 256 bytes** regardless of how much information it holds.
|
||||
-238
@@ -1,238 +0,0 @@
|
||||
This document outlines how to configure NuVotifier for a variety of different
|
||||
configurations. It should be used by server administrators looking to set up
|
||||
NuVotifier on their network.
|
||||
|
||||
# Key vs Token
|
||||
|
||||
This document uses the terms `token` and `key`. These are two separate terms,
|
||||
and it is important to understand the differences.
|
||||
|
||||
## Key
|
||||
|
||||
Keys in terms of NuVotifier refer to the old Votifier style RSA keys. They
|
||||
are typically distributed as a pair of files - a public and private key. When
|
||||
dealing with RSA keys, you can share your public key with server lists, but make
|
||||
sure to keep your private key safe with you. You can find these keys
|
||||
auto-generated under `plugins/(Nu)Votifier/rsa/`, where your public key is
|
||||
named `public.key`, while your private key is labeled `private.key`. Make sure
|
||||
not to separate the two, as they won't work together.
|
||||
|
||||
## Token
|
||||
|
||||
Tokens in terms of NuVotifier refer to a new short string of characters. This
|
||||
string of characters is secret, and should never be exposed to anyone but you or
|
||||
server lists which support NuVotifier voting (check with the server list
|
||||
first). Tokens are most commonly used internally within a network for use when
|
||||
using the proxy vote forwarding method.
|
||||
|
||||
# Single Server Configuration (Bukkit / Spigot / Sponge)
|
||||
|
||||
You will want to set up your server using these instructions when you only have
|
||||
one Minecraft server (Bukkit / Sponge). These are the most trivial installation
|
||||
steps.
|
||||
|
||||
## Installing the plugin on a server
|
||||
|
||||
To install NuVotifier, you should get a copy from one of the following
|
||||
locations:
|
||||
|
||||
+ [Github](https://github.com/parallelblock/NuVotifier/releases) - Official
|
||||
release location
|
||||
+ [SpigotMC](https://www.spigotmc.org/resources/nuvotifier.13449/) - SpigotMC
|
||||
release mirror
|
||||
+ [repo.parallelblock.com](https://repo.parallelblock.com/repository/public-raw/nuvotifier/nuvotifier.jar) - ParallelBlock CI 'hot off the presses' releases
|
||||
|
||||
The NuVotifier jar should then be placed within the `plugins/` folder of your
|
||||
Minecraft server. When you then start your server, NuVotifier will generate
|
||||
the necessary configuration and initial keys for you.
|
||||
|
||||
## Configuring the plugin
|
||||
|
||||
When running in a single server configuration, NuVotifier's default
|
||||
configuration works well. The only value which you may need to change is the
|
||||
`port` configuration value - this specifies the port on which NuVotifier
|
||||
listens to new votes. This port cannot be any port you are already using. For
|
||||
example, if Minecraft is running on the default port (25565), you cannot set
|
||||
`port` also to 25565. Make sure port is a number from 1-65535.
|
||||
|
||||
You can then add your server to server lists using the port you configured while
|
||||
also supplying your public key. At this point, you should be able to send a test
|
||||
vote to your server!
|
||||
|
||||
# Multi-server / Network configuration
|
||||
|
||||
You will want to use these instructions when you have more than one server and
|
||||
are using a Minecraft proxy (such as BungeeCord, LilyPad, or Velocity).
|
||||
|
||||
## Single-Proxy BungeeCord/Velocity Networks
|
||||
|
||||
When running a single-proxy network, NuVotifier is able to operate as both
|
||||
a vote receiver (emitting events for proxy based vote listeners) as well as
|
||||
a vote forwarder. NuVotifier's vote forwarding logic is also advanced enough
|
||||
to be used in a wide variety of ways.
|
||||
|
||||
### Installing the plugin on the servers
|
||||
|
||||
NuVotifier should be installed on each of the servers you wish to process votes
|
||||
on, as well as the proxy server. The proxy's NuVotifier server will act as the
|
||||
public facing Votifier instance, where as the Minecraft servers will instead
|
||||
receive votes forwarded from the proxy's NuVotifier.
|
||||
|
||||
Like the single server installation, you can download NuVotifier at any one of
|
||||
the following locations:
|
||||
|
||||
+ [Github](https://github.com/parallelblock/NuVotifier/releases) - Official
|
||||
release location
|
||||
+ [SpigotMC](https://www.spigotmc.org/resources/nuvotifier.13449/) - SpigotMC
|
||||
release mirror
|
||||
+ [repo.parallelblock.com](https://repo.parallelblock.com/repository/public-raw/nuvotifier/nuvotifier.jar) - ParallelBlock CI 'hot off the presses' releases
|
||||
|
||||
The plugin should then be placed in the `plugins/` directory of the Bungeecord
|
||||
server as well as each of the Minecraft servers.
|
||||
|
||||
### Vote Forwarding
|
||||
|
||||
The following sections describe how vote forwarding works. Vote forwarding is an
|
||||
advanced configurable operation, and requires a lot of configuration to get
|
||||
correct.
|
||||
|
||||
### Plugin messaging based forwarding
|
||||
|
||||
This section describes how to set up plugin messaging forwarding. This is
|
||||
recommended if you have just one proxy, however this may also work with
|
||||
some exceptions in multi-proxy networks.
|
||||
|
||||
First off, we should configure each of the Minecraft servers. Open each of the
|
||||
servers' NuVotifier configurations. Each one should have `method` under the
|
||||
`forwarding` configuration section set to `pluginMessaging`. Under the
|
||||
`pluginMessaging` configuration section, `channel` is set to `nuvotifier:votes`.
|
||||
Since 1.13, Spigot has decided that channels must now contain a colon. While
|
||||
this is a completely senseless change, it is easier to simply conform and change
|
||||
the channel name. This is a safe default (and is protected from injection by a
|
||||
hacked Minecraft client), but feel free to change it. If you choose to change
|
||||
it, make sure you change it everywhere all around your network!
|
||||
|
||||
Since we are receiving forwarded votes through plugin messaging and not through
|
||||
network ports, we should set `port` on each of the Minecraft servers to -1. This
|
||||
will disable NuVotifier's port, while still allowing it to receive forwarded
|
||||
plugin messaging votes. This will only work when using plugin messaging!
|
||||
|
||||
Since we have all of that configured, we can now move to the proxy's NuVotifier
|
||||
configuration. It is once again located under `plugins/NuVotifier/` directory
|
||||
(`config.yml` for BungeeCord, `config.toml` for Velocity). Like the single server,
|
||||
`port` should be set to an unbound port. The default `8192` works well, however
|
||||
you may have to change it depending on your hosting provider.
|
||||
|
||||
Since we are using plugin messaging based forwarding, lets set `method` under
|
||||
`forwarding` to `pluginMessaging`. Make sure that `channel` is the same channel
|
||||
that you configured your servers to listen to - if you didn't change this value,
|
||||
then the default will work.
|
||||
|
||||
NuVotifier saves when it can't immediately forward in a user defined cache. In
|
||||
almost all installations this should be `file`, however, some installations may
|
||||
instead want `none` or `memory`. If you don't know, the `cache` should be set to
|
||||
its default, `file`.
|
||||
|
||||
By default, NuVotifier will forward the vote to all of the connected Minecraft
|
||||
servers (all servers you can get to when typing /server). You can manually
|
||||
exclude some of these servers by first uncommenting `excludedServers`, then
|
||||
adding the server's name to the `excludedServers` list. After adding, NuVotifier
|
||||
will not send any votes to this server. If you only send the vote to the server
|
||||
the player joined, these servers are ignored.
|
||||
|
||||
NuVotifier can also selectively send the vote to only the server which the
|
||||
player has joined. This option can be turned on by setting
|
||||
`onlySendToJoinedServer` to true. When this option is on, the vote will attempt
|
||||
to go to the server which the player is currently on. If the player is currently
|
||||
not on a server (offline), then NuVotifier will then attempt to send the vote to
|
||||
the `joinedServerFallback`. If the vote cannot be sent to the
|
||||
`joinedServerFallback`, then the vote is saved in the cache until the vote can
|
||||
be sent to the `joinedServerFallback` server - note this will not send it to the
|
||||
next server the player joins! If you do not want a fallback server and would
|
||||
rather the vote be applied when the player next joins, set
|
||||
`joinedServerFallback` to `''`. This will cause NuVotifier to save the vote
|
||||
until the player joins again.
|
||||
|
||||
By default, votes are saved for 10 days in the cache. This time can be increased
|
||||
or decreased by changing the `voteTTL` field. This number can be as high or low
|
||||
as you want. It is measured in days within the cache.
|
||||
|
||||
### Proxy based vote forwarding
|
||||
|
||||
In more complex network situations, it may be required to instead use the proxy
|
||||
based vote forwarding. This configuration is less flexible and more difficult to
|
||||
set up properly - most people should use plugin messaging based forwarding.
|
||||
Proxy based forwarding does not support vote caching or advanced forwarding
|
||||
configurations. If you cannot use plugin messaging forwarding, then the
|
||||
following section will describe how to set up proxy based vote forwarding. If
|
||||
you still require advanced forwarding features or more advanced behaviors, it
|
||||
may be worthwhile to write your own plugin to perform the proper forwarding logic
|
||||
(you will know if you need to do this).
|
||||
|
||||
In proxy based vote forwarding, one of your proxy servers acts as the head vote
|
||||
receiver. This server will be the one you configure the voting websites to
|
||||
vote to. You can set up multiple vote receivers and load balance between them,
|
||||
however this configuration has limited benefit and is probably more work than it
|
||||
is worth. Instead of communicating through plugin messaging like the plugin
|
||||
messaging forwarding strategy, proxy forwarding instead acts like a voting
|
||||
website to each of the backend servers and duplicates the vote it receives to
|
||||
each of the backend servers it is configured with.
|
||||
|
||||
First, we will set up all of the Minecraft servers before setting up the head
|
||||
vote receiver. For each of the servers, NuVotifier should be configured to bind
|
||||
to an unused port. This port must be valid. If you have multiple Votifiers on
|
||||
the same IP/server, make sure that the port which you are using between
|
||||
NuVotifier instances are different. Remember the the port number as we will need
|
||||
it later to insert into the proxy NuVotifier configuration. In addition to the
|
||||
port number, you will also need the default token - this is the string auto
|
||||
generated by NuVotifier under the `default` key under `tokens`. Since we are using
|
||||
proxy forwarding, change `disable-v1-protocol` to true - we will only be using the
|
||||
new NuVotifier protocol within our network (vote receiver -> minecraft server).
|
||||
Once all configured, that is all we need to do. The rest of the defaults will work.
|
||||
|
||||
Now we will configure the vote receiving NuVotifier proxy server. The port
|
||||
should be configured as described many times above, being a unique port. Instead
|
||||
of setting `method` to `pluginMessaging` like above, we will set `method` to
|
||||
`proxy`. This will tell NuVotifier that we instead want to proxy the votes
|
||||
instead of forward them through plugin messaging channels.
|
||||
|
||||
The last step is to set up the proxy forwaring settings. Under the `proxy`
|
||||
section is a place to enter each server. Create a unique name for each of the
|
||||
server's you will be forwarding votes to. The default proxy NuVotifier
|
||||
configuration already has two Minecraft servers filled in - Hub and Hub2. You
|
||||
should replace these with your own servers - the names of the servers do not
|
||||
matter. Under each of these servers, we need to configure `address`, `port`, and
|
||||
`token`. `address` is the IP address of the Bukkit/Sponge server. This may be
|
||||
`localhost` or `127.0.0.1` if the Minecraft server is on the same physical
|
||||
server as the vote receiving NuVotifier server. `port` is the port of the
|
||||
Bukkit/Sponge NuVotifier server. `token` is the default token of the
|
||||
Bukkit/Sponge NuVotifier server. This can be found within its configuration,
|
||||
usually under the `token` section. Once the servers are configured, NuVotifier
|
||||
will forward all votes it receives to the servers configured under the `proxy`
|
||||
section.
|
||||
|
||||
# Debugging NuVotifier
|
||||
|
||||
NuVotifier can be difficult to set up in some configurations. If you find you
|
||||
don't know what is wrong, you may want to try setting `debug` in each of the
|
||||
server's NuVotifier configurations to `true`. This will increase logging within
|
||||
your console.
|
||||
|
||||
# Reloading NuVotifier
|
||||
|
||||
For Bukkit and Bungee, you may reload NuVotifier through the `/nvreload` command
|
||||
in your console only - this command will not work in-game by design. Make sure to
|
||||
ensure that it completed correctly before assuming NuVotifier works!
|
||||
|
||||
# Default Configurations
|
||||
|
||||
If you find you have messed up NuVotifier beyond repair, you can grab a default
|
||||
configuration from the following locations (depending on the server it is
|
||||
installed on):
|
||||
|
||||
+ [Bukkit](https://github.com/parallelblock/NuVotifier/blob/master/bukkit/src/main/resources/bukkitConfig.yml)
|
||||
+ [Sponge](https://github.com/parallelblock/NuVotifier/blob/master/sponge/src/main/resources/com/vexsoftware/votifier/sponge/spongeConfig.yml)
|
||||
+ [Bungeecord](https://github.com/parallelblock/NuVotifier/blob/master/bungeecord/src/main/resources/bungeeConfig.yml)
|
||||
|
||||
You can also regenerate this configuration by deleting the old configuration and
|
||||
restarting the server.
|
||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip
|
||||
+18
-43
@@ -1,52 +1,27 @@
|
||||
// This file contains special build instructions specifically for the ParallelBlock CI in order to instruct it how to
|
||||
// build the project. Usually, this shouldn't be interacted with by a human.
|
||||
|
||||
import com.mashape.unirest.http.*
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.mashape.unirest:unirest-java:1.4.9'
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: "maven-publish"
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name 'parallelBlock'
|
||||
if (project.version.endsWith("-SNAPSHOT"))
|
||||
url "$System.env.SNAPSHOT_REPO"
|
||||
else
|
||||
url "$System.env.RELEASE_REPO"
|
||||
credentials {
|
||||
username "$System.env.REPO_USERNAME"
|
||||
password "$System.env.REPO_PASSWORD"
|
||||
}
|
||||
buildscript {
|
||||
}
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'com.jfrog.bintray'
|
||||
|
||||
bintray {
|
||||
user = System.getenv("BINTRAY_USER")
|
||||
key = System.getenv("BINTRAY_KEY")
|
||||
pkg {
|
||||
repo = 'public'
|
||||
name = 'nuvotifier'
|
||||
version {
|
||||
name = project.version
|
||||
}
|
||||
publications = ['DefaultPublication']
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
versioned(MavenPublication) {
|
||||
DefaultPublication(MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (System.env.LATEST_BUILD) {
|
||||
task publishLatestToParallelBlockRaw {
|
||||
inputs.files(configurations.archives.artifacts.files)
|
||||
outputs.upToDateWhen {false}
|
||||
doLast {
|
||||
configurations.archives.artifacts.each {
|
||||
def resp = Unirest.put("$System.env.RAW_UPLOAD_PATH/${it.name}.${it.extension}")
|
||||
.basicAuth("$System.env.REPO_USERNAME", "$System.env.REPO_PASSWORD")
|
||||
.body(it.file.bytes).asString()
|
||||
assert resp.status >= 200 && resp.status <= 299
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-1
@@ -1,4 +1,18 @@
|
||||
plugins {
|
||||
id 'org.spongepowered.plugin' version '0.9.0'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly group: "org.spongepowered", name: "spongeapi", version: "7.0.0-SNAPSHOT"
|
||||
compileOnly group: "org.spongepowered", name: "spongeapi", version: "7.1.0"
|
||||
annotationProcessor group: "org.spongepowered", name: "spongeapi", version: "7.1.0"
|
||||
compile project(":nuvotifier-common")
|
||||
}
|
||||
|
||||
sponge {
|
||||
plugin {
|
||||
id = 'nuvotifier'
|
||||
meta {
|
||||
version = project.version
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.vexsoftware.votifier.sponge;
|
||||
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class SLF4JLogger implements LoggingAdapter {
|
||||
|
||||
private final Logger l;
|
||||
public SLF4JLogger(Logger l) {
|
||||
this.l = l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String s) {
|
||||
l.error(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, Object... o) {
|
||||
l.error(s, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String s) {
|
||||
l.warn(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String s, Object... o) {
|
||||
l.warn(s, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String s) {
|
||||
l.info(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String s, Object... o) {
|
||||
l.info(s, o);
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,13 @@ import com.vexsoftware.votifier.net.VotifierServerBootstrap;
|
||||
import com.vexsoftware.votifier.net.VotifierSession;
|
||||
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAIO;
|
||||
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAKeygen;
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
|
||||
import com.vexsoftware.votifier.sponge.config.ConfigLoader;
|
||||
import com.vexsoftware.votifier.sponge.event.VotifierEvent;
|
||||
import com.vexsoftware.votifier.sponge.forwarding.ForwardedVoteListener;
|
||||
import com.vexsoftware.votifier.sponge.forwarding.ForwardingVoteSink;
|
||||
import com.vexsoftware.votifier.sponge.forwarding.SpongePluginMessagingForwardingSink;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardingVoteSink;
|
||||
import com.vexsoftware.votifier.util.KeyCreator;
|
||||
import io.netty.channel.Channel;
|
||||
import org.slf4j.Logger;
|
||||
@@ -29,13 +30,15 @@ import java.security.KeyPair;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Plugin(id = "nuvotifier", name = "NuVotifier", version = "2.3.7", authors = "ParallelBlock LLC",
|
||||
@Plugin(id = "nuvotifier", name = "NuVotifier", version="SOMEONE_FUCKED_UP_THE_BUILD", authors = "Ichbinjoe",
|
||||
description = "Safe, smart, and secure Votifier server plugin")
|
||||
public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.platform.VotifierPlugin, ForwardedVoteListener {
|
||||
|
||||
@Inject
|
||||
public Logger logger;
|
||||
|
||||
private SLF4JLogger loggerAdapter;
|
||||
|
||||
@Inject
|
||||
@ConfigDir(sharedRoot = false)
|
||||
public File configDir;
|
||||
@@ -45,6 +48,7 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.pla
|
||||
@Listener
|
||||
public void onServerStart(GameStartedServerEvent event) {
|
||||
this.scheduler = new SpongeScheduler(this);
|
||||
this.loggerAdapter = new SLF4JLogger(logger);
|
||||
|
||||
// Load configuration.
|
||||
ConfigLoader.loadConfig(this);
|
||||
@@ -79,8 +83,9 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.pla
|
||||
// Initialize the receiver.
|
||||
final String host = ConfigLoader.getSpongeConfig().host;
|
||||
final int port = ConfigLoader.getSpongeConfig().port;
|
||||
if (debug)
|
||||
logger.info("DEBUG mode enabled!");
|
||||
|
||||
if (!debug)
|
||||
logger.info("QUIET mode enabled!");
|
||||
|
||||
if (port >= 0) {
|
||||
final boolean disablev1 = ConfigLoader.getSpongeConfig().disableV1Protocol;
|
||||
@@ -168,8 +173,8 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.pla
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getPluginLogger() {
|
||||
return logger;
|
||||
public LoggingAdapter getPluginLogger() {
|
||||
return loggerAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,7 @@ public class SpongeConfig {
|
||||
|
||||
@Setting(comment = "Whether or not to print debug messages. In a production system, this should be set to false.\n" +
|
||||
"This is useful when initially setting up NuVotifier to ensure votes are being delivered.")
|
||||
public boolean debug = false;
|
||||
public boolean debug = true;
|
||||
|
||||
@Setting( value = "disable-v1-protocol", comment = "Setting this option to true will disable handling of Protocol v1 packets. While the old protocol is not secure, this\n" +
|
||||
"option is currently not recommended as most voting sites only support the old protocol at present. However, if you are\n" +
|
||||
@@ -49,7 +49,7 @@ public class SpongeConfig {
|
||||
public static class PluginMessaging {
|
||||
|
||||
@Setting
|
||||
public String channel = "NuVotifier:votes";
|
||||
public String channel = "nuvotifier:votes";
|
||||
}
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package com.vexsoftware.votifier.sponge.forwarding;
|
||||
|
||||
/**
|
||||
* Represents a method at which to receive forwarded votes.
|
||||
*/
|
||||
public interface ForwardingVoteSink {
|
||||
|
||||
/**
|
||||
* Stop or close any oustanding network interfaces. Occurs on the onDisable method of a plugin.
|
||||
*/
|
||||
void halt();
|
||||
|
||||
}
|
||||
+6
-13
@@ -1,9 +1,8 @@
|
||||
package com.vexsoftware.votifier.sponge.forwarding;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.sponge.VotifierPlugin;
|
||||
import com.vexsoftware.votifier.util.GsonInst;
|
||||
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSink;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
|
||||
import org.spongepowered.api.Platform;
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.network.ChannelBinding;
|
||||
@@ -11,19 +10,16 @@ import org.spongepowered.api.network.ChannelBuf;
|
||||
import org.spongepowered.api.network.RawDataListener;
|
||||
import org.spongepowered.api.network.RemoteConnection;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SpongePluginMessagingForwardingSink implements ForwardingVoteSink, RawDataListener {
|
||||
public class SpongePluginMessagingForwardingSink extends AbstractPluginMessagingForwardingSink implements RawDataListener {
|
||||
|
||||
public SpongePluginMessagingForwardingSink(VotifierPlugin p, String channel, ForwardedVoteListener listener) {
|
||||
super(listener);
|
||||
this.channelBinding = Sponge.getChannelRegistrar().createRawChannel(p, channel);
|
||||
this.channelBinding.addListener(Platform.Type.SERVER, this);
|
||||
this.listener = listener;
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
private final VotifierPlugin p;
|
||||
private final ForwardedVoteListener listener;
|
||||
private final ChannelBinding.RawDataChannel channelBinding;
|
||||
|
||||
@Override
|
||||
@@ -33,12 +29,9 @@ public class SpongePluginMessagingForwardingSink implements ForwardingVoteSink,
|
||||
|
||||
@Override
|
||||
public void handlePayload(ChannelBuf channelBuf, RemoteConnection remoteConnection, Platform.Type type) {
|
||||
byte[] msgDirBuf = channelBuf.readBytes(channelBuf.available());
|
||||
try {
|
||||
byte[] msgDirBuf = channelBuf.readBytes(channelBuf.available());
|
||||
String message = new String(msgDirBuf, StandardCharsets.UTF_8);
|
||||
JsonObject jsonObject = GsonInst.gson.fromJson(message, JsonObject.class);
|
||||
Vote v = new Vote(jsonObject);
|
||||
listener.onForward(v);
|
||||
this.handlePluginMessage(msgDirBuf);
|
||||
} catch (Exception e) {
|
||||
p.getLogger().error("There was an unknown error when processing a forwarded vote.", e);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,6 @@ host: %ip%
|
||||
# Port to listen for new votes on
|
||||
port: 8192
|
||||
|
||||
# Whether or not to print debug messages. In a production system, this should be set to false.
|
||||
# This is useful when initially setting up NuVotifier to ensure votes are being delivered.
|
||||
debug: false
|
||||
|
||||
# Setting this option to true will disable handling of Protocol v1 packets. While the old protocol is not secure, this
|
||||
# option is currently not recommended as most voting sites only support the old protocol at present. However, if you are
|
||||
# using NuVotifier's proxy forwarding mechanism, enabling this option will increase your server's security.
|
||||
@@ -26,4 +22,4 @@ forwarding:
|
||||
# - pluginMessaging - Sets up plugin messaging
|
||||
method: none
|
||||
pluginMessaging:
|
||||
channel: NuVotifier:votes
|
||||
channel: nuvotifier:votes
|
||||
+36
-3
@@ -1,3 +1,10 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator
|
||||
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.regex.Pattern
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
@@ -17,12 +24,18 @@ dependencies {
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
dependencies {
|
||||
// All current implementations include Google Guava already, and explicitly shading in Guava causes the plugin
|
||||
// to fail to initialize on Sponge.
|
||||
exclude(dependency('com.google.guava:guava:24.1-jre'))
|
||||
}
|
||||
|
||||
baseName = 'nuvotifier'
|
||||
classifier = null
|
||||
relocate "io.netty", "com.vexsoftware.votifier.netty"
|
||||
relocate "org.json", "com.vexsoftware.votifier.json"
|
||||
relocate "com.google.code", "com.vexsoftware.votifier.google"
|
||||
relocate "com.google.guava", "com.vexsoftware.votifier.google"
|
||||
relocate new SimpleRelocator("com.google.code", "com.vexsoftware.votifier.google.code", new ArrayList<String>(), Collections.singletonList("com.vexsoftware.votifier.sponge"))
|
||||
relocate "com.google.guava", "com.vexsoftware.votifier.google.guava"
|
||||
relocate "org.apache.commons.io", "com.vexsoftware.votifier.commons.io"
|
||||
}
|
||||
|
||||
@@ -34,4 +47,24 @@ artifacts {
|
||||
}
|
||||
}
|
||||
|
||||
build.dependsOn shadowJar
|
||||
def artifactPattern = Pattern.compile(/(.*)-([0-9]+\.)*[0-9]+(-SNAPSHOT)?\.jar/)
|
||||
|
||||
task('linkLatest') {
|
||||
dependsOn tasks.shadowJar
|
||||
doLast {
|
||||
tasks.shadowJar.outputs.files.each {
|
||||
System.println(it.absolutePath)
|
||||
def noVerMatcher = artifactPattern.matcher(it.absolutePath)
|
||||
if (!noVerMatcher.matches())
|
||||
throw new IllegalStateException("wtf")
|
||||
|
||||
def noVer = Paths.get(noVerMatcher.group(1) + ".jar")
|
||||
def versnd = it.toPath()
|
||||
try {
|
||||
Files.createSymbolicLink(noVer, versnd)
|
||||
} catch (FileAlreadyExistsException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build.dependsOn linkLatest
|
||||
@@ -1,4 +1,5 @@
|
||||
dependencies {
|
||||
compileOnly group: "com.velocitypowered", name: "velocity-api", version: "1.0-SNAPSHOT"
|
||||
annotationProcessor group: "com.velocitypowered", name: "velocity-api", version: "1.0-SNAPSHOT"
|
||||
compile project(":nuvotifier-common")
|
||||
}
|
||||
+21
-8
@@ -1,14 +1,16 @@
|
||||
package com.vexsoftware.votifier.velocity;
|
||||
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.event.player.ServerConnectedEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.messages.MessageHandler;
|
||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.platform.BackendServer;
|
||||
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSource;
|
||||
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -17,26 +19,30 @@ public final class OnlineForwardPluginMessagingForwardingSource extends Abstract
|
||||
|
||||
private final String fallbackServer;
|
||||
private final VotifierPlugin plugin;
|
||||
private final ChannelIdentifier velocityChannelId;
|
||||
|
||||
public OnlineForwardPluginMessagingForwardingSource(String channel, VotifierPlugin plugin, VoteCache cache, String fallbackServer) {
|
||||
super(channel, plugin, cache);
|
||||
public OnlineForwardPluginMessagingForwardingSource(String channel, ServerFilter serverFilter, VotifierPlugin plugin, VoteCache cache, String fallbackServer, int dumpRate) {
|
||||
super(channel, serverFilter, plugin, cache, dumpRate);
|
||||
this.fallbackServer = fallbackServer;
|
||||
this.plugin = plugin;
|
||||
this.velocityChannelId = VelocityUtil.getId(channel);
|
||||
plugin.getServer().getChannelRegistrar().register(velocityChannelId);
|
||||
plugin.getServer().getEventManager().register(plugin, this);
|
||||
plugin.getServer().getChannelRegistrar().register((source, side, identifier, data) -> MessageHandler.ForwardStatus.HANDLED, VelocityUtil.getId(channel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forward(Vote v) {
|
||||
Optional<Player> p = plugin.getServer().getPlayer(v.getUsername());
|
||||
Optional<ServerConnection> sc = p.flatMap(Player::getCurrentServer);
|
||||
if (sc.isPresent()) {
|
||||
if (forwardSpecific(new VelocityBackendServer(plugin.getServer(), sc.get().getServerInfo()), v)) {
|
||||
if (sc.isPresent() &&
|
||||
serverFilter.isAllowed(sc.get().getServerInfo().getName())
|
||||
) {
|
||||
if (forwardSpecific(new VelocityBackendServer(plugin.getServer(), sc.get().getServer()), v)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Optional<ServerInfo> fs = plugin.getServer().getServerInfo(fallbackServer);
|
||||
Optional<RegisteredServer> fs = plugin.getServer().getServer(fallbackServer);
|
||||
// nowhere to fall back to, yet still not online. lets save this vote yet!
|
||||
if (!fs.isPresent())
|
||||
attemptToAddToPlayerCache(v, v.getUsername());
|
||||
@@ -50,4 +56,11 @@ public final class OnlineForwardPluginMessagingForwardingSource extends Abstract
|
||||
onServerConnect(server);
|
||||
handlePlayerSwitch(server, e.getPlayer().getUsername());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPluginMessage(PluginMessageEvent e) {
|
||||
if (e.getIdentifier().equals(velocityChannelId)) {
|
||||
e.setResult(PluginMessageEvent.ForwardResult.handled());
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-6
@@ -1,26 +1,35 @@
|
||||
package com.vexsoftware.votifier.velocity;
|
||||
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.event.player.ServerConnectedEvent;
|
||||
import com.velocitypowered.api.proxy.messages.MessageHandler;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSource;
|
||||
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource {
|
||||
|
||||
private final VotifierPlugin plugin;
|
||||
private final ChannelIdentifier velocityChannelId;
|
||||
|
||||
public PluginMessagingForwardingSource(String channel, List<String> ignoredServers, VotifierPlugin plugin, VoteCache cache) {
|
||||
super(channel, ignoredServers, plugin, cache);
|
||||
public PluginMessagingForwardingSource(String channel, ServerFilter serverFilter, VotifierPlugin plugin, VoteCache cache, int dumpRate) {
|
||||
super(channel, serverFilter, plugin, cache, dumpRate);
|
||||
this.plugin = plugin;
|
||||
this.velocityChannelId = VelocityUtil.getId(channel);
|
||||
plugin.getServer().getChannelRegistrar().register(velocityChannelId);
|
||||
plugin.getServer().getEventManager().register(plugin, this);
|
||||
plugin.getServer().getChannelRegistrar().register((source, side, identifier, data) -> MessageHandler.ForwardStatus.HANDLED, VelocityUtil.getId(channel));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onServerConnected(final ServerConnectedEvent e) { //Attempt to resend any votes that were previously cached.
|
||||
onServerConnect(new VelocityBackendServer(plugin.getServer(), e.getServer()));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPluginMessage(PluginMessageEvent e) {
|
||||
if (e.getIdentifier().equals(velocityChannelId)) {
|
||||
e.setResult(PluginMessageEvent.ForwardResult.handled());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.vexsoftware.votifier.velocity;
|
||||
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class SLF4JLogger implements LoggingAdapter {
|
||||
|
||||
private final Logger l;
|
||||
public SLF4JLogger(Logger l) {
|
||||
this.l = l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String s) {
|
||||
l.error(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, Object... o) {
|
||||
l.error(s, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String s) {
|
||||
l.warn(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String s, Object... o) {
|
||||
l.warn(s, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String s) {
|
||||
l.info(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String s, Object... o) {
|
||||
l.info(s, o);
|
||||
}
|
||||
}
|
||||
+7
-19
@@ -1,42 +1,30 @@
|
||||
package com.vexsoftware.votifier.velocity;
|
||||
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.vexsoftware.votifier.platform.BackendServer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
class VelocityBackendServer implements BackendServer {
|
||||
private final ProxyServer server;
|
||||
private final ServerInfo info;
|
||||
private final RegisteredServer rs;
|
||||
|
||||
VelocityBackendServer(ProxyServer server, ServerInfo info) {
|
||||
VelocityBackendServer(ProxyServer server, RegisteredServer rs) {
|
||||
this.server = server;
|
||||
this.info = info;
|
||||
this.rs = rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return info.getName();
|
||||
return rs.getServerInfo().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendPluginMessage(String channel, byte[] data) {
|
||||
Optional<ServerConnection> connection = server.getAllPlayers().stream()
|
||||
.map(p -> p.getCurrentServer().filter(s -> s.getServerInfo().equals(info)))
|
||||
.filter(Optional::isPresent)
|
||||
.findAny()
|
||||
.flatMap(o -> o);
|
||||
if (connection.isPresent()) {
|
||||
connection.get().sendPluginMessage(VelocityUtil.getId(channel), data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return rs.sendPluginMessage(VelocityUtil.getId(channel), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return info.getName();
|
||||
return rs.getServerInfo().getName();
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import com.moandjiezana.toml.Toml;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.vexsoftware.votifier.VoteHandler;
|
||||
@@ -16,9 +15,11 @@ import com.vexsoftware.votifier.net.VotifierSession;
|
||||
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAIO;
|
||||
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAKeygen;
|
||||
import com.vexsoftware.votifier.platform.BackendServer;
|
||||
import com.vexsoftware.votifier.platform.LoggingAdapter;
|
||||
import com.vexsoftware.votifier.platform.ProxyVotifierPlugin;
|
||||
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
|
||||
import com.vexsoftware.votifier.support.forwarding.ForwardingVoteSource;
|
||||
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.FileVoteCache;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.MemoryVoteCache;
|
||||
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
|
||||
@@ -45,12 +46,11 @@ import java.security.KeyPair;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Plugin(id = "nuvotifier", name = "NuVotifier", version = "2.3.7", authors = "ParallelBlock LLC",
|
||||
description = "Safe, smart, and secure Votifier server plugin")
|
||||
public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
|
||||
@Inject
|
||||
public Logger logger;
|
||||
private LoggingAdapter loggingAdapter;
|
||||
|
||||
@Inject
|
||||
@DataDirectory
|
||||
@@ -64,6 +64,7 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
@Subscribe
|
||||
public void onServerStart(ProxyInitializeEvent event) {
|
||||
this.scheduler = new VelocityScheduler(server, this);
|
||||
this.loggingAdapter = new SLF4JLogger(logger);
|
||||
|
||||
// Load configuration.
|
||||
Toml config;
|
||||
@@ -92,7 +93,10 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
debug = config.getBoolean("debug");
|
||||
if (config.contains("quiet"))
|
||||
debug = !config.getBoolean("quiet");
|
||||
else
|
||||
debug = config.getBoolean("debug", true);
|
||||
|
||||
// Load Votifier tokens.
|
||||
config.getTable("tokens").toMap().forEach((service, key) -> {
|
||||
@@ -105,8 +109,8 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
// Initialize the receiver.
|
||||
final String host = config.getString("host");
|
||||
final int port = Math.toIntExact(config.getLong("port"));
|
||||
if (debug)
|
||||
logger.info("DEBUG mode enabled!");
|
||||
if (!debug)
|
||||
logger.info("QUIET mode enabled!");
|
||||
|
||||
final boolean disablev1 = config.getBoolean("disable-v1-protocol", false);
|
||||
if (disablev1) {
|
||||
@@ -131,7 +135,9 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
if ("none".equals(cacheMethod)) {
|
||||
getLogger().info("Vote cache none selected for caching: votes that cannot be immediately delivered will be lost.");
|
||||
} else if ("memory".equals(cacheMethod)) {
|
||||
voteCache = new MemoryVoteCache(server.getAllServers().size());
|
||||
voteCache = new MemoryVoteCache(
|
||||
server.getAllServers().size(), this,
|
||||
fwdCfg.getTable("memory-cache").getLong("cacheTime", -1L));
|
||||
getLogger().info("Using in-memory cache for votes that are not able to be delivered.");
|
||||
} else if ("file".equals(cacheMethod)) {
|
||||
try {
|
||||
@@ -143,11 +149,16 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
getLogger().error("Unload to load file cache. Votes will be lost!", e);
|
||||
}
|
||||
}
|
||||
if (!pmCfg.getBoolean("onlySendToJoinedServer")) {
|
||||
List<String> ignoredServers = pmCfg.getList("excludedServers");
|
||||
int dumpRate = pmCfg.getLong("dumpRate", 5L).intValue();
|
||||
|
||||
ServerFilter filter = new ServerFilter(
|
||||
pmCfg.getList("excludedServers"),
|
||||
pmCfg.getBoolean("whitelist", false)
|
||||
);
|
||||
|
||||
if (!pmCfg.getBoolean("onlySendToJoinedServer")) {
|
||||
try {
|
||||
forwardingMethod = new PluginMessagingForwardingSource(channel, ignoredServers, this, voteCache);
|
||||
forwardingMethod = new PluginMessagingForwardingSource(channel, filter, this, voteCache, dumpRate);
|
||||
getLogger().info("Forwarding votes over PluginMessaging channel '" + channel + "' for vote forwarding!");
|
||||
} catch (RuntimeException e) {
|
||||
getLogger().error("NuVotifier could not set up PluginMessaging for vote forwarding!", e);
|
||||
@@ -156,7 +167,7 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
try {
|
||||
String fallbackServer = pmCfg.getString("joinedServerFallback", null);
|
||||
if (fallbackServer != null && fallbackServer.isEmpty()) fallbackServer = null;
|
||||
forwardingMethod = new OnlineForwardPluginMessagingForwardingSource(channel, this, voteCache, fallbackServer);
|
||||
forwardingMethod = new OnlineForwardPluginMessagingForwardingSource(channel, filter, this, voteCache, fallbackServer, dumpRate);
|
||||
} catch (RuntimeException e) {
|
||||
getLogger().error("NuVotifier could not set up PluginMessaging for vote forwarding!", e);
|
||||
}
|
||||
@@ -232,8 +243,7 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
// Initialize the configuration file.
|
||||
String cfgStr = new String(ByteStreams.toByteArray(VotifierPlugin.class.getResourceAsStream("/config.toml")), StandardCharsets.UTF_8);
|
||||
String token = TokenUtil.newToken();
|
||||
// TODO: Velocity doesn't expose any bind address, so use a temporary workaround
|
||||
cfgStr = cfgStr.replace("%ip%", "0.0.0.0");
|
||||
cfgStr = cfgStr.replace("%ip%", server.getBoundAddress().getAddress().getHostAddress());
|
||||
cfgStr = cfgStr.replace("%default_token%", token);
|
||||
|
||||
/*
|
||||
@@ -299,8 +309,8 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getPluginLogger() {
|
||||
return logger;
|
||||
public LoggingAdapter getPluginLogger() {
|
||||
return loggingAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -359,6 +369,6 @@ public class VotifierPlugin implements VoteHandler, ProxyVotifierPlugin {
|
||||
|
||||
@Override
|
||||
public Optional<BackendServer> getServer(String name) {
|
||||
return server.getServerInfo(name).map(s -> new VelocityBackendServer(server, s));
|
||||
return server.getServer(name).map(s -> new VelocityBackendServer(server, s));
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,6 @@ host = "%ip%"
|
||||
# Port to listen for new votes on
|
||||
port = 8192
|
||||
|
||||
# Whether or not to print debug messages. In a production system, this should be set to false.
|
||||
# This is useful when initially setting up NuVotifier to ensure votes are being delivered.
|
||||
debug = false
|
||||
|
||||
# Setting this option to true will disable handling of Protocol v1 packets. While the old protocol is not secure, this
|
||||
# option is currently not recommended as most voting sites only support the old protocol at present. However, if you are
|
||||
# using NuVotifier's proxy forwarding mechanism, enabling this option will increase your server's security.
|
||||
@@ -28,17 +24,20 @@ default = "%default_token%"
|
||||
method = "none"
|
||||
|
||||
[forwarding.pluginMessaging]
|
||||
channel = "NuVotifier:votes"
|
||||
channel = "nuvotifier:votes"
|
||||
|
||||
# Only send votes to certain servers. If a server is present on excludedServers, then votes will NOT be forwarded
|
||||
# to them. If excludedServers is not present, votes will be sent to all servers.
|
||||
# If onlySendToJoinedServer is true, this option is ignored.
|
||||
# If onlySendToJoinedServer is true and a player is on one of these servers, NuVotifier will process the vote as if the
|
||||
# player was not on the network at all.
|
||||
# excludedServers = ["server1", "server2"]
|
||||
|
||||
# If uncommented, turns excludedServers into an includedServers, where only servers on the list will be used.
|
||||
# whitelist = true
|
||||
|
||||
# Sets which cache to use if there are no players on the server that the vote is being sent to. Supported caches:
|
||||
# - none - Votes will simply be lost when no players are present on the server.
|
||||
# - memory - Votes will be cached in memory when no players are present. When Bungee is restarted, the votes will be lost.
|
||||
# This method is NOT recommended for production systems!
|
||||
# - file - Votes will be saved to a local file every 3 minutes and on shutdown and reloaded when Bungee is restarted. This is suitable for
|
||||
# most set ups.
|
||||
cache = "file"
|
||||
@@ -50,11 +49,21 @@ onlySendToJoinedServer = false
|
||||
# ONLY USED IF onlySendToJoinedServer is true!!
|
||||
joinedServerFallback = "Hub"
|
||||
|
||||
# Defines how quickly to dump votes over a player's connection while offloading a cache in votes per second
|
||||
dumpRate = 5
|
||||
|
||||
[forwarding.file-cache]
|
||||
# Options for file caching.
|
||||
name = "cached-votes.json"
|
||||
# days before a vote is considered 'dead' - removed from cache with a console warning
|
||||
voteTTL = 10
|
||||
# days before a vote is considered 'dead' - removed from cache with a console warning. -1 signifies no cache.
|
||||
cacheTime = 10
|
||||
|
||||
[forwarding.memory-cache]
|
||||
# Options for file caching.
|
||||
name = "cached-votes.json"
|
||||
# days before a vote is considered 'dead' - removed from cache with a console warning. -1 signifies no cache. Votes are
|
||||
# also removed on restart
|
||||
cacheTime = 10
|
||||
|
||||
[forwarding.proxy]
|
||||
# Specify servers to proxy votes for.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"id":"nuvotifier",
|
||||
"name":"NuVotifier",
|
||||
"version":"@app.version@",
|
||||
"description":"Safe, smart, and secure Votifier server plugin",
|
||||
"authors":["Ichbinjoe"],
|
||||
"dependencies":[],
|
||||
"main":"com.vexsoftware.votifier.velocity.VotifierPlugin"
|
||||
}
|
||||
Reference in new issue
Block a user