More modifications, back to basics

This commit is contained in:
Joe Hirschfeld committed 2020-02-06 22:16:19 -08:00
1 parent 3a2cdfd7ea
commit eefd80005e
107 files changed
+49 -5258

No files matched your search

@@ -1,3 +1,4 @@
dependencies {
api group: "org.spigotmc", name: "spigot-api", version: "1.8.8-R0.1-SNAPSHOT"
implementation project(":model")
}
@@ -1,5 +1,6 @@
package com.vexsoftware.votifier.model;
import io.ibj.nuvotifier.model.NuVote;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
@@ -19,14 +20,14 @@ public class VotifierEvent extends Event {
/**
* Encapsulated vote record.
*/
private Vote vote;
private NuVote vote;
/**
* Constructs a vote event that encapsulated the given vote record.
*
* @param vote vote record
*/
public VotifierEvent(final Vote vote) {
public VotifierEvent(final NuVote vote) {
this.vote = vote;
}
-4
View File
@@ -1,4 +0,0 @@
dependencies {
compile project(":common")
}
@@ -1,57 +0,0 @@
package com.vexsoftware.votifier;
import io.ibj.nuvotifier.platform.platform.scheduler.ScheduledVotifierTask;
import io.ibj.nuvotifier.platform.platform.scheduler.VotifierScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.util.concurrent.TimeUnit;
class BukkitScheduler implements VotifierScheduler {
private final NuVotifierBukkit plugin;
public BukkitScheduler(NuVotifierBukkit plugin) {
this.plugin = plugin;
}
private int toTicks(int time, TimeUnit unit) {
return (int) (unit.toMillis(time) / 50);
}
@Override
public ScheduledVotifierTask sync(Runnable runnable) {
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTask(plugin, runnable));
}
@Override
public ScheduledVotifierTask onPool(Runnable runnable) {
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTaskAsynchronously(plugin, runnable));
}
@Override
public ScheduledVotifierTask delayedSync(Runnable runnable, int delay, TimeUnit unit) {
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTaskLater(plugin, runnable, toTicks(delay, unit)));
}
@Override
public ScheduledVotifierTask delayedOnPool(Runnable runnable, int delay, TimeUnit unit) {
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, runnable, toTicks(delay, unit)));
}
@Override
public ScheduledVotifierTask repeatOnPool(Runnable runnable, int delay, int repeat, TimeUnit unit) {
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, runnable, toTicks(delay, unit), toTicks(repeat, unit)));
}
private static class BukkitTaskWrapper implements ScheduledVotifierTask {
private final BukkitTask task;
private BukkitTaskWrapper(BukkitTask task) {
this.task = task;
}
@Override
public void cancel() {
task.cancel();
}
}
}
@@ -1,380 +0,0 @@
/*
* Copyright (C) 2012 Vex Software LLC
* This file is part of Votifier.
*
* Votifier is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Votifier is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Votifier. If not, see <http://www.gnu.org/licenses/>.
*/
package com.vexsoftware.votifier;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.vexsoftware.votifier.cmd.NVReloadCmd;
import com.vexsoftware.votifier.cmd.TestVoteCmd;
import com.vexsoftware.votifier.forwarding.BukkitPluginMessagingForwardingSink;
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.server.VotifierServerBootstrap;
import com.vexsoftware.votifier.net.server.VotifierSession;
import io.ibj.nuvotifier.legacyserver.protocol.v1crypto.RSAIO;
import io.ibj.nuvotifier.legacyserver.protocol.v1crypto.RSAKeygen;
import io.ibj.nuvotifier.platform.platform.JavaUtilLogger;
import io.ibj.nuvotifier.platform.platform.LoggingAdapter;
import io.ibj.nuvotifier.platform.platform.VotifierPlugin;
import io.ibj.nuvotifier.platform.platform.scheduler.VotifierScheduler;
import com.vexsoftware.votifier.util.KeyCreator;
import com.vexsoftware.votifier.util.TokenUtil;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyPair;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* The main Votifier plugin class.
*
* @author Blake Beaupain
* @author Kramer Campbell
*/
public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, VotifierPlugin, ForwardedVoteListener {
/**
* The Votifier instance.
*/
private static NuVotifierBukkit instance;
/**
* The current Votifier version.
*/
private String version;
/**
* The server bootstrap.
*/
private VotifierServerBootstrap bootstrap;
/**
* The RSA key pair.
*/
private KeyPair keyPair;
/**
* Debug mode flag
*/
private boolean debug;
/**
* Keys used for websites.
*/
private Map<String, Key> tokens = new HashMap<>();
private ForwardingVoteSink forwardingMethod;
private VotifierScheduler scheduler;
private LoggingAdapter pluginLogger;
private boolean loadAndBind() {
scheduler = new BukkitScheduler(this);
pluginLogger = new JavaUtilLogger(getLogger());
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
// Handle configuration.
File config = new File(getDataFolder(), "config.yml");
/*
* Use IP address from server.properties as a default for
* configurations. Do not use InetAddress.getLocalHost() as it most
* likely will return the main server address instead of the address
* assigned to the server.
*/
String hostAddr = Bukkit.getServer().getIp();
if (hostAddr == null || hostAddr.length() == 0)
hostAddr = "0.0.0.0";
/*
* Create configuration file if it does not exists; otherwise, load it
*/
if (!config.exists()) {
try {
// First time run - do some initialization.
getLogger().info("Configuring Votifier for the first time...");
// Initialize the configuration file.
config.createNewFile();
// Load and manually replace variables in the configuration.
String cfgStr = new String(ByteStreams.toByteArray(getResource("bukkitConfig.yml")), StandardCharsets.UTF_8);
String token = TokenUtil.newToken();
cfgStr = cfgStr.replace("%default_token%", token).replace("%ip%", hostAddr);
Files.asCharSink(config, StandardCharsets.UTF_8).write(cfgStr);
/*
* Remind hosted server admins to be sure they have the right
* port number.
*/
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Assigning NuVotifier to listen on port 8192. If you are hosting Craftbukkit on a");
getLogger().info("shared server please check with your hosting provider to verify that this port");
getLogger().info("is available for your use. Chances are that your hosting provider will assign");
getLogger().info("a different port, which you need to specify in config.yml");
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Your default NuVotifier token is " + token + ".");
getLogger().info("You will need to provide this token when you submit your server to a voting");
getLogger().info("list.");
getLogger().info("------------------------------------------------------------------------------");
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Error creating configuration file", ex);
return false;
}
}
YamlConfiguration cfg;
File rsaDirectory = new File(getDataFolder(), "rsa");
// Load configuration.
cfg = YamlConfiguration.loadConfiguration(config);
/*
* Create RSA directory and keys if it does not exist; otherwise, read
* keys.
*/
try {
if (!rsaDirectory.exists()) {
rsaDirectory.mkdir();
keyPair = RSAKeygen.generate(2048);
RSAIO.save(rsaDirectory, keyPair);
} else {
keyPair = RSAIO.load(rsaDirectory);
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,
"Error reading configuration file or RSA tokens", ex);
return 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");
if (tokenSection != null) {
Map<String, Object> websites = tokenSection.getValues(false);
for (Map.Entry<String, Object> website : websites.entrySet()) {
tokens.put(website.getKey(), KeyCreator.createKeyFrom(website.getValue().toString()));
getLogger().info("Loaded token for website: " + website.getKey());
}
} else {
String token = TokenUtil.newToken();
tokenSection = cfg.createSection("tokens");
tokenSection.set("default", token);
tokens.put("default", KeyCreator.createKeyFrom(token));
try {
cfg.save(config);
} catch (IOException e) {
getLogger().log(Level.SEVERE,
"Error generating Votifier token", e);
return false;
}
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("No tokens were found in your configuration, so we've generated one for you.");
getLogger().info("Your default Votifier token is " + token + ".");
getLogger().info("You will need to provide this token when you submit your server to a voting");
getLogger().info("list.");
getLogger().info("------------------------------------------------------------------------------");
}
// Initialize the receiver.
final String host = cfg.getString("host", hostAddr);
final int port = cfg.getInt("port", 8192);
if (!debug)
getLogger().info("QUIET mode enabled!");
if (port >= 0) {
final boolean disablev1 = cfg.getBoolean("disable-v1-protocol");
if (disablev1) {
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Votifier protocol v1 parsing has been disabled. Most voting websites do not");
getLogger().info("currently support the modern Votifier protocol in NuVotifier.");
getLogger().info("------------------------------------------------------------------------------");
}
this.bootstrap = new VotifierServerBootstrap(host, port, this, disablev1);
this.bootstrap.start(error -> {});
} else {
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Your Votifier port is less than 0, so we assume you do NOT want to start the");
getLogger().info("votifier port server! Votifier will not listen for votes over any port, and");
getLogger().info("will only listen for pluginMessaging forwarded votes!");
getLogger().info("------------------------------------------------------------------------------");
}
ConfigurationSection forwardingConfig = cfg.getConfigurationSection("forwarding");
if (forwardingConfig != null) {
String method = forwardingConfig.getString("method", "none").toLowerCase(); //Default to lower case for case-insensitive searches
if ("none".equals(method)) {
getLogger().info("Method none selected for vote forwarding: Votes will not be received from a forwarder.");
} else if ("pluginmessaging".equals(method)) {
String channel = forwardingConfig.getString("pluginMessaging.channel", "NuVotifier");
try {
forwardingMethod = new BukkitPluginMessagingForwardingSink(this, channel, this);
getLogger().info("Receiving votes over PluginMessaging channel '" + channel + "'.");
} catch (RuntimeException e) {
getLogger().log(Level.SEVERE, "NuVotifier could not set up PluginMessaging for vote forwarding!", e);
}
} else {
getLogger().severe("No vote forwarding method '" + method + "' known. Defaulting to noop implementation.");
}
}
return true;
}
private void halt() {
// Shut down the network handlers.
if (bootstrap != null) {
bootstrap.shutdown();
bootstrap = null;
}
if (forwardingMethod != null) {
forwardingMethod.halt();
forwardingMethod = null;
}
}
@Override
public void onEnable() {
NuVotifierBukkit.instance = this;
// Set the plugin version.
version = getDescription().getVersion();
getCommand("nvreload").setExecutor(new NVReloadCmd(this));
getCommand("testvote").setExecutor(new TestVoteCmd(this));
if (!loadAndBind()) {
gracefulExit();
setEnabled(false); // safer to just bomb out
}
}
@Override
public void onDisable() {
halt();
getLogger().info("Votifier disabled.");
}
public boolean reload() {
try {
halt();
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "On halt, an exception was thrown. This may be fine!", ex);
}
if (loadAndBind()) {
getLogger().info("Reload was successful.");
return true;
} else {
try {
halt();
getLogger().log(Level.SEVERE, "On reload, there was a problem with the configuration. Votifier currently does nothing!");
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "On reload, there was a problem loading, and we could not re-halt the server. Votifier is in an unstable state!", ex);
}
return false;
}
}
private void gracefulExit() {
getLogger().log(Level.SEVERE, "Votifier did not initialize properly!");
}
/**
* Gets the instance.
*
* @return The instance
*/
public static NuVotifierBukkit getInstance() {
return instance;
}
@Override
public LoggingAdapter getPluginLogger() {
return pluginLogger;
}
@Override
public VotifierScheduler getScheduler() {
return scheduler;
}
public boolean isDebug() {
return debug;
}
@Override
public Map<String, Key> getTokens() {
return tokens;
}
@Override
public KeyPair getProtocolV1Key() {
return keyPair;
}
@Override
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) {
if (debug) {
getLogger().info("Got a " + protocolVersion.humanReadable + " vote record from " + remoteAddress + " -> " + vote);
}
Bukkit.getScheduler().runTask(this, () -> Bukkit.getPluginManager().callEvent(new VotifierEvent(vote)));
}
@Override
public void onError(Throwable throwable, boolean alreadyHandledVote, String remoteAddress) {
if (debug) {
if (alreadyHandledVote) {
getLogger().log(Level.SEVERE, "Vote processed, however an exception " +
"occurred with a vote from " + remoteAddress, throwable);
} else {
getLogger().log(Level.SEVERE, "Unable to process vote from " + remoteAddress, throwable);
}
} else if (!alreadyHandledVote) {
getLogger().log(Level.SEVERE, "Unable to process vote from " + remoteAddress);
}
}
@Override
public void onForward(final Vote v) {
if (debug) {
getLogger().info("Got a forwarded vote -> " + v);
}
Bukkit.getScheduler().runTask(this, () -> Bukkit.getPluginManager().callEvent(new VotifierEvent(v)));
}
}
@@ -1,31 +0,0 @@
package com.vexsoftware.votifier.cmd;
import com.vexsoftware.votifier.NuVotifierBukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class NVReloadCmd implements CommandExecutor {
private final NuVotifierBukkit plugin;
public NVReloadCmd(NuVotifierBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender.hasPermission("nuvotifier.reload")) {
sender.sendMessage(ChatColor.GRAY + "Reloading NuVotifier...");
if (plugin.reload()) {
sender.sendMessage(ChatColor.DARK_GREEN + "NuVotifier has been reloaded!");
} else {
sender.sendMessage(ChatColor.DARK_RED + "Looks like there was a problem reloading NuVotifier, check the console!");
}
} else {
sender.sendMessage(ChatColor.DARK_RED + "You do not have permission to do this!");
}
return true;
}
}
@@ -1,40 +0,0 @@
package com.vexsoftware.votifier.cmd;
import com.vexsoftware.votifier.NuVotifierBukkit;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierSession;
import com.vexsoftware.votifier.util.ArgsToVote;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class TestVoteCmd implements CommandExecutor {
private final NuVotifierBukkit plugin;
public TestVoteCmd(NuVotifierBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender.hasPermission("nuvotifier.testvote")) {
Vote v;
try {
v = ArgsToVote.parse(args);
} catch (IllegalArgumentException e) {
sender.sendMessage(ChatColor.DARK_RED + "Error while parsing arguments to create test vote: " + e.getMessage());
sender.sendMessage(ChatColor.GRAY + "Usage hint: /testvote [username] [serviceName=?] [username=?] [address=?] [localTimestamp=?] [timestamp=?]");
return true;
}
plugin.onVoteReceived(v, VotifierSession.ProtocolVersion.TEST, "localhost.test");
sender.sendMessage(ChatColor.GREEN + "Test vote executed: " + v.toString());
} else {
sender.sendMessage(ChatColor.DARK_RED + "You do not have permission to do this!");
}
return true;
}
}
@@ -1,43 +0,0 @@
package com.vexsoftware.votifier.forwarding;
import com.vexsoftware.votifier.NuVotifierBukkit;
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.util.logging.Level;
/**
* Created by Joe Hirschfeld on 10/20/2015.
*/
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.p = p;
}
private final Plugin p;
private final String channel;
@Override
public void halt() {
Bukkit.getMessenger().unregisterIncomingPluginChannel(p, channel, this);
}
@Override
public void onPluginMessageReceived(String s, Player player, byte[] bytes) {
try {
this.handlePluginMessage(bytes);
} catch (Exception e) {
p.getLogger().log(Level.SEVERE, "There was an unknown error when processing a forwarded vote.", e);
}
}
}
@@ -1,25 +0,0 @@
# The IP to listen to. Use 0.0.0.0 if you wish to listen to all interfaces on your server. (All IP addresses)
# This defaults to the IP you have configured your server to listen on, or 0.0.0.0 if you have not configured this.
host: %ip%
# Port to listen for new votes on
port: 8192
# 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.
disable-v1-protocol: false
# All tokens, labeled by the serviceName of each server list.
tokens:
# Default token for all server lists, if another isn't supplied.
default: %default_token%
# Configuration section for all vote forwarding to NuVotifier
forwarding:
# Sets whether to set up a remote method for fowarding. Supported methods:
# - none - Does not set up a forwarding method.
# - pluginMessaging - Sets up plugin messaging
method: none
pluginMessaging:
channel: nuvotifier:votes
-26
View File
@@ -1,26 +0,0 @@
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: [Ichbinjoe, blakeman8192, Kramer, tuxed]
api-version: 1.13
commands:
nvreload:
description: Reloads the NuVotifier configuration
permission: nuvotifier.reload
permission-message: "You do not have permission to run this command."
usage: "/nvreload"
testvote:
description: Sends a test vote to the server
permission: nuvotifier.testvote
permission-message: "You do not have permission to run this command."
usage: "/testvote [username] [serviceName=?] [username=?] [address=?] [localTimestamp=?] [timestamp=?]"
permissions:
nuvotifier.reload:
description: Allows you to reload the NuVotifier plugin
default: op
nuvotifier.testvote:
description: Allows you to send a test vote
default: op
+4
View File
@@ -0,0 +1,4 @@
dependencies {
api group: "net.md-5", name: "bungeecord-api", version: "1.8-SNAPSHOT"
implementation project(":model")
}
@@ -1,6 +1,6 @@
package com.vexsoftware.votifier.bungee.events;
import com.vexsoftware.votifier.model.Vote;
import io.ibj.nuvotifier.model.NuVote;
import net.md_5.bungee.api.plugin.Event;
/**
@@ -11,13 +11,13 @@ public class VotifierEvent extends Event {
/**
* The vote.
**/
private final Vote vote;
private final NuVote vote;
public VotifierEvent(Vote vote) {
public VotifierEvent(NuVote vote) {
this.vote = vote;
}
public Vote getVote() {
public NuVote getVote() {
return vote;
}
}
-4
View File
@@ -1,4 +0,0 @@
dependencies {
compileOnly group: "net.md-5", name: "bungeecord-api", version: "1.8-SNAPSHOT"
compile project(":common")
}
@@ -1,28 +0,0 @@
package com.vexsoftware.votifier.bungee;
import com.google.common.base.Preconditions;
import io.ibj.nuvotifier.platform.platform.BackendServer;
import net.md_5.bungee.api.config.ServerInfo;
public class BungeeBackendServer implements BackendServer {
private final ServerInfo info;
public BungeeBackendServer(ServerInfo info) {
this.info = Preconditions.checkNotNull(info, "info");
}
@Override
public String getName() {
return info.getName();
}
@Override
public boolean sendPluginMessage(String channel, byte[] data) {
return info.sendData(channel, data, false);
}
@Override
public String toString() {
return info.getName();
}
}
@@ -1,53 +0,0 @@
package com.vexsoftware.votifier.bungee;
import io.ibj.nuvotifier.platform.platform.scheduler.ScheduledVotifierTask;
import io.ibj.nuvotifier.platform.platform.scheduler.VotifierScheduler;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import java.util.concurrent.TimeUnit;
class BungeeScheduler implements VotifierScheduler {
private final NuVotifier plugin;
public BungeeScheduler(NuVotifier plugin) {
this.plugin = plugin;
}
@Override
public ScheduledVotifierTask sync(Runnable runnable) {
return onPool(runnable);
}
@Override
public ScheduledVotifierTask onPool(Runnable runnable) {
return new BungeeTaskWrapper(plugin.getProxy().getScheduler().runAsync(plugin, runnable));
}
@Override
public ScheduledVotifierTask delayedSync(Runnable runnable, int delay, TimeUnit unit) {
return delayedOnPool(runnable, delay, unit);
}
@Override
public ScheduledVotifierTask delayedOnPool(Runnable runnable, int delay, TimeUnit unit) {
return new BungeeTaskWrapper(plugin.getProxy().getScheduler().schedule(plugin, runnable, delay, unit));
}
@Override
public ScheduledVotifierTask repeatOnPool(Runnable runnable, int delay, int repeat, TimeUnit unit) {
return new BungeeTaskWrapper(plugin.getProxy().getScheduler().schedule(plugin, runnable, delay, repeat, unit));
}
private static class BungeeTaskWrapper implements ScheduledVotifierTask {
private final ScheduledTask task;
private BungeeTaskWrapper(ScheduledTask task) {
this.task = task;
}
@Override
public void cancel() {
this.task.cancel();
}
}
}
@@ -1,414 +0,0 @@
package com.vexsoftware.votifier.bungee;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.vexsoftware.votifier.VoteHandler;
import com.vexsoftware.votifier.bungee.cmd.NVReloadCmd;
import com.vexsoftware.votifier.bungee.cmd.TestVoteCmd;
import com.vexsoftware.votifier.net.server.VotifierServerBootstrap;
import io.ibj.nuvotifier.platform.platform.BackendServer;
import io.ibj.nuvotifier.platform.platform.JavaUtilLogger;
import io.ibj.nuvotifier.platform.platform.LoggingAdapter;
import io.ibj.nuvotifier.platform.platform.ProxyVotifierPlugin;
import com.vexsoftware.votifier.bungee.events.VotifierEvent;
import io.ibj.nuvotifier.platform.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;
import com.vexsoftware.votifier.support.forwarding.proxy.ProxyForwardingVoteSource;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierSession;
import io.ibj.nuvotifier.legacyserver.protocol.v1crypto.RSAIO;
import io.ibj.nuvotifier.legacyserver.protocol.v1crypto.RSAKeygen;
import com.vexsoftware.votifier.util.KeyCreator;
import com.vexsoftware.votifier.util.TokenUtil;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyPair;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlugin {
/**
* The server channel.
*/
private VotifierServerBootstrap bootstrap;
/**
* The RSA key pair.
*/
private KeyPair keyPair;
/**
* Debug mode flag
*/
private boolean debug;
/**
* Keys used for websites.
*/
private Map<String, Key> tokens = new HashMap<>();
/**
* Method used to forward votes to downstream servers
*/
private ForwardingVoteSource forwardingMethod;
private VotifierScheduler scheduler;
private LoggingAdapter pluginLogger;
private void loadAndBind() {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
// Handle configuration.
File config = new File(getDataFolder() , "config.yml");
File rsaDirectory = new File(getDataFolder() , "rsa");
Configuration configuration;
if (!config.exists()) {
try {
// First time run - do some initialization.
getLogger().info("Configuring Votifier for the first time...");
// Initialize the configuration file.
config.createNewFile();
String cfgStr = new String(ByteStreams.toByteArray(getResourceAsStream("bungeeConfig.yml")), StandardCharsets.UTF_8);
String token = TokenUtil.newToken();
cfgStr = cfgStr.replace("%default_token%", token);
Files.asCharSink(config, StandardCharsets.UTF_8).write(cfgStr);
/*
* Remind hosted server admins to be sure they have the right
* port number.
*/
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Assigning NuVotifier to listen on port 8192. If you are hosting BungeeCord on a");
getLogger().info("shared server please check with your hosting provider to verify that this port");
getLogger().info("is available for your use. Chances are that your hosting provider will assign");
getLogger().info("a different port, which you need to specify in config.yml");
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Assigning NuVotifier to listen to interface 0.0.0.0. This is usually alright,");
getLogger().info("however, if you want NuVotifier to only listen to one interface for security ");
getLogger().info("reasons (or you use a shared host), you may change this in the config.yml.");
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Your default Votifier token is " + token + ".");
getLogger().info("You will need to provide this token when you submit your server to a voting");
getLogger().info("list.");
getLogger().info("------------------------------------------------------------------------------");
} catch (Exception ex) {
throw new RuntimeException("Unable to create configuration file", ex);
}
}
// Load the configuration.
try {
configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config);
} catch (IOException e) {
throw new RuntimeException("Unable to load configuration", e);
}
/*
* Create RSA directory and keys if it does not exist; otherwise, read
* keys.
*/
try {
if (!rsaDirectory.exists()) {
rsaDirectory.mkdir();
keyPair = RSAKeygen.generate(2048);
RSAIO.save(rsaDirectory, keyPair);
} else {
keyPair = RSAIO.load(rsaDirectory);
}
} catch (Exception ex) {
throw new RuntimeException("Error reading RSA tokens", ex);
}
// Load Votifier tokens.
Configuration tokenSection = configuration.getSection("tokens");
if (configuration.get("tokens") != null) {
for (String s : tokenSection.getKeys()) {
tokens.put(s, KeyCreator.createKeyFrom(tokenSection.getString(s)));
getLogger().info("Loaded token for website: " + s);
}
} else {
String token = TokenUtil.newToken();
configuration.set("tokens", ImmutableMap.of("default", token));
tokens.put("default", KeyCreator.createKeyFrom(token));
try {
ConfigurationProvider.getProvider(YamlConfiguration.class).save(configuration, config);
} catch (IOException e) {
throw new RuntimeException("Error generating Votifier token", e);
}
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("No tokens were found in your configuration, so we've generated one for you.");
getLogger().info("Your default Votifier token is " + token + ".");
getLogger().info("You will need to provide this token when you submit your server to a voting");
getLogger().info("list.");
getLogger().info("------------------------------------------------------------------------------");
}
// Initialize the receiver.
final String host = configuration.getString("host", "0.0.0.0");
final int port = configuration.getInt("port", 8192);
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) {
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Votifier protocol v1 parsing has been disabled. Most voting websites do not");
getLogger().info("currently support the modern Votifier protocol in NuVotifier.");
getLogger().info("------------------------------------------------------------------------------");
}
// Must set up server asynchronously due to BungeeCord goofiness.
FutureTask<?> initTask = new FutureTask<>(Executors.callable(() -> {
this.bootstrap = new VotifierServerBootstrap(host, port, NuVotifier.this, disablev1);
this.bootstrap.start(err -> {});
}));
getProxy().getScheduler().runAsync(this, initTask);
try {
initTask.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException("Unable to start server", e);
}
Configuration fwdCfg = configuration.getSection("forwarding");
String fwdMethod = fwdCfg.getString("method", "none").toLowerCase();
if ("none".equals(fwdMethod)) {
getLogger().info("Method none selected for vote forwarding: Votes will not be forwarded to backend servers.");
} else if ("pluginmessaging".equals(fwdMethod)) {
String channel = fwdCfg.getString("pluginMessaging.channel", "NuVotifier");
String cacheMethod = fwdCfg.getString("pluginMessaging.cache", "file").toLowerCase();
VoteCache voteCache = null;
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(), 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 {
voteCache = new FileVoteCache(
ProxyServer.getInstance().getServers().size(), this,
new File(getDataFolder(), fwdCfg.getString("pluginMessaging.file.name")),
fwdCfg.getInt("pluginMessaging.file.cacheTime", -1));
} catch (IOException e) {
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")) {
try {
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);
}
} else {
try {
String fallbackServer = fwdCfg.getString("pluginMessaging.joinedServerFallback", null);
if (fallbackServer != null && fallbackServer.isEmpty()) fallbackServer = null;
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);
}
}
} else if ("proxy".equals(fwdMethod)) {
Configuration serverSection = fwdCfg.getSection("proxy");
List<ProxyForwardingVoteSource.BackendServer> serverList = new ArrayList<>();
for (String s : serverSection.getKeys()) {
Configuration section = serverSection.getSection(s);
InetAddress address;
try {
address = InetAddress.getByName(section.getString("address"));
} catch (UnknownHostException e) {
getLogger().info("Address " + section.getString("address") + " couldn't be looked up. Ignoring!");
continue;
}
Key token = null;
try {
token = KeyCreator.createKeyFrom(section.getString("token", section.getString("key")));
} catch (IllegalArgumentException e) {
getLogger().log(Level.SEVERE,
"An exception occurred while attempting to add proxy target '" + s + "' - maybe your token is wrong? " +
"Votes will not be forwarded to this server!", e);
}
if (token != null) {
ProxyForwardingVoteSource.BackendServer server = new ProxyForwardingVoteSource.BackendServer(s,
new InetSocketAddress(address, section.getInt("port")),
token);
serverList.add(server);
}
}
forwardingMethod = new ProxyForwardingVoteSource(this, bootstrap::client, serverList, null);
getLogger().info("Forwarding votes from this NuVotifier instance to another NuVotifier server.");
} else {
getLogger().severe("No vote forwarding method '" + fwdMethod + "' known. Defaulting to noop implementation.");
}
}
@Override
public void onEnable() {
scheduler = new BungeeScheduler(this);
pluginLogger = new JavaUtilLogger(getLogger());
PluginManager pm = ProxyServer.getInstance().getPluginManager();
pm.registerCommand(this, new NVReloadCmd(this));
pm.registerCommand(this, new TestVoteCmd(this));
loadAndBind();
}
private void halt() {
// Shut down the network handlers.
if (bootstrap != null) {
bootstrap.shutdown();
bootstrap = null;
}
if (forwardingMethod != null) {
forwardingMethod.halt();
forwardingMethod = null;
}
}
public boolean reload() {
try {
halt();
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "On halt, an exception was thrown. This may be fine!", ex);
}
try {
loadAndBind();
getLogger().info("Reload was successful.");
return true;
} catch (Exception ex) {
try {
halt();
getLogger().log(Level.SEVERE, "On reload, there was a problem with the configuration. Votifier currently does nothing!", ex);
} catch (Exception ex2) {
getLogger().log(Level.SEVERE, "On reload, there was a problem loading, and we could not re-halt the server. Votifier is in an unstable state!", ex);
getLogger().log(Level.SEVERE, "(halt exception)", ex2);
}
return false;
}
}
@Override
public void onDisable() {
halt();
getLogger().info("Votifier disabled.");
}
@Override
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
getLogger().info("Got a protocol v1 vote record from " + remoteAddress + " -> " + vote);
} else {
getLogger().info("Got a protocol v2 vote record from " + remoteAddress + " -> " + vote);
}
}
getProxy().getScheduler().runAsync(this, () -> getProxy().getPluginManager().callEvent(new VotifierEvent(vote)));
if (forwardingMethod != null) {
getProxy().getScheduler().runAsync(this, () -> forwardingMethod.forward(vote));
}
}
@Override
public void onError(Throwable throwable, boolean alreadyHandledVote, String remoteAddress) {
if (debug) {
if (alreadyHandledVote) {
getLogger().log(Level.SEVERE, "Vote processed, however an exception " +
"occurred with a vote from " + remoteAddress, throwable);
} else {
getLogger().log(Level.SEVERE, "Unable to process vote from " + remoteAddress, throwable);
}
} else if (!alreadyHandledVote) {
getLogger().log(Level.SEVERE, "Unable to process vote from " + remoteAddress);
}
}
@Override
public Map<String, Key> getTokens() {
return tokens;
}
@Override
public KeyPair getProtocolV1Key() {
return keyPair;
}
@Override
public LoggingAdapter getPluginLogger() {
return pluginLogger;
}
@Override
public VotifierScheduler getScheduler() {
return scheduler;
}
public boolean isDebug() {
return debug;
}
@Override
public Collection<BackendServer> getAllBackendServers() {
return getProxy().getServers().values().stream()
.map(BungeeBackendServer::new)
.collect(Collectors.toList());
}
@Override
public Optional<BackendServer> getServer(String name) {
ServerInfo info = getProxy().getServerInfo(name);
return Optional.ofNullable(info).map(BungeeBackendServer::new);
}
}
@@ -1,60 +0,0 @@
package com.vexsoftware.votifier.bungee;
import com.vexsoftware.votifier.model.Vote;
import io.ibj.nuvotifier.platform.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 net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
/**
* @author Joseph Hirschfeld
* @date 12/31/2015
*/
public final class OnlineForwardPluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
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);
}
private final String fallbackServer;
@Override
public void forward(Vote v) {
ProxiedPlayer p = ProxyServer.getInstance().getPlayer(v.getUsername());
if (p != null && p.getServer() != null &&
serverFilter.isAllowed(p.getServer().getInfo().getName())) {
if (forwardSpecific(new BungeeBackendServer(p.getServer().getInfo()), v)) {
return;
}
}
ServerInfo serverInfo = ProxyServer.getInstance().getServers().get(fallbackServer);
// nowhere to fall back to, yet still not online. lets save this vote yet!
if (serverInfo == null)
attemptToAddToPlayerCache(v, v.getUsername());
else if (!forwardSpecific(new BungeeBackendServer(serverInfo), v))
attemptToAddToCache(v, fallbackServer);
}
@EventHandler
public void onPluginMessage(PluginMessageEvent e) {
if (e.getTag().equals(channel)) e.setCancelled(true);
}
@EventHandler
public void onServerConnected(final ServerConnectedEvent e) { //Attempt to resend any votes that were previously cached.
BackendServer server = new BungeeBackendServer(e.getServer().getInfo());
handlePlayerSwitch(server, e.getPlayer().getName());
onServerConnect(server);
}
}
@@ -1,33 +0,0 @@
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;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
public class PluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
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, int dumpRate) {
super(channel, nuVotifier, voteCache, dumpRate);
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
}
@EventHandler
public void onPluginMessage(PluginMessageEvent e) {
if (e.getTag().equals(channel)) e.setCancelled(true);
}
@EventHandler
public void onServerConnected(final ServerConnectedEvent e) { //Attempt to resend any votes that were previously cached.
onServerConnect(new BungeeBackendServer(e.getServer().getInfo()));
}
}
@@ -1,44 +0,0 @@
package com.vexsoftware.votifier.bungee.cmd;
import com.vexsoftware.votifier.bungee.NuVotifier;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class NVReloadCmd extends Command {
private final NuVotifier plugin;
private static final BaseComponent reloadingNuVotifier = new TextComponent("Reloading NuVotifier...");
private static final BaseComponent nuvotifierHasBeenReloaded = new TextComponent("NuVotifier has been reloaded!");
private static final BaseComponent problem = new TextComponent("Looks like there was a problem reloading NuVotifier, check the console!");
private static final BaseComponent permission = new TextComponent("You do not have permission to do this!");
static {
reloadingNuVotifier.setColor(ChatColor.GRAY);
nuvotifierHasBeenReloaded.setColor(ChatColor.DARK_GREEN);
problem.setColor(ChatColor.DARK_RED);
permission.setColor(ChatColor.DARK_RED);
}
public NVReloadCmd(NuVotifier plugin) {
super("pnvreload", "nuvotifier.reload");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (sender.hasPermission("nuvotifier.reload")) {
sender.sendMessage(reloadingNuVotifier);
if (plugin.reload()) {
sender.sendMessage(nuvotifierHasBeenReloaded);
} else {
sender.sendMessage(problem);
}
} else {
sender.sendMessage(permission);
}
}
}
@@ -1,51 +0,0 @@
package com.vexsoftware.votifier.bungee.cmd;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierSession;
import com.vexsoftware.votifier.util.ArgsToVote;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class TestVoteCmd extends Command {
private final NuVotifier plugin;
private static final BaseComponent permission = new TextComponent("You do not have permission to do this!");
private static final BaseComponent usage = new TextComponent("Usage hint: /ptestvote [username] [serviceName=?] [username=?] [address=?] [localTimestamp=?] [timestamp=?]");
static {
usage.setColor(ChatColor.GRAY);
}
public TestVoteCmd(NuVotifier plugin) {
super("ptestvote", "nuvotifier.testvote");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (sender.hasPermission("nuvotifier.testvote")) {
Vote v;
try {
v = ArgsToVote.parse(args);
} catch (IllegalArgumentException e) {
TextComponent c = new TextComponent("Error while parsing arguments to create test vote: " + e.getMessage());
c.setColor(ChatColor.DARK_RED);
sender.sendMessage(c);
sender.sendMessage(usage);
return;
}
plugin.onVoteReceived(v, VotifierSession.ProtocolVersion.TEST, "localhost.test");
TextComponent c = new TextComponent("Test vote executed: " + v.toString());
c.setColor(ChatColor.GREEN);
sender.sendMessage(c);
} else {
sender.sendMessage(permission);
}
}
}
-4
View File
@@ -1,4 +0,0 @@
name: NuVotifier
main: com.vexsoftware.votifier.bungee.NuVotifier
version: @app.version@
author: Ichbinjoe, blakeman8192, Kramer, tuxed
@@ -1,70 +0,0 @@
# The IP to listen to. Use 0.0.0.0 if you wish to listen to all interfaces on your server. (All IP addresses)
# This defaults to the IP you have configured your server to listen on, or 0.0.0.0 if you have not configured this.
host: 0.0.0.0
# Port to listen for new votes on
port: 8192
# All tokens, labeled by the serviceName of each server list.
tokens:
# Default token for all server lists, if another isn't supplied.
default: %default_token%
# 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.
disable-v1-protocol: false
# Configuration section for all vote forwarding to NuVotifier
forwarding:
# Sets whether to set up a remote method for fowarding. Supported methods:
# - none - Does not set up a forwarding method.
# - pluginMessaging - Sets up plugin messaging.
# - proxy - Proxies votes to other NuVotifier servers from this server.
method: none
pluginMessaging:
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 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.
# - 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
# Only sends the vote to the server in which the player is currently joined.
onlySendToJoinedServer: false
#Sets which server to send the vote in the event the player is not online when the vote is cast
#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
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:
address: 127.0.0.1
port: 8193
token: MyKeyHere
Hub2:
address: 127.0.0.1
port: 8194
token: AnotherKeyHere
-7
View File
@@ -1,7 +0,0 @@
dependencies {
compile project(":model")
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'
testCompile group: "org.json", name: "json", version: "20180130" // retain this for testing reasons
}
@@ -1,56 +0,0 @@
package com.vexsoftware.votifier.util;
import com.vexsoftware.votifier.model.Vote;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ArgsToVote {
private static final Pattern argumentPattern = Pattern.compile("([a-zA-Z]+)=(\\S*)");
public static Vote parse(String[] arguments) {
return parse(arguments, null);
}
public static Vote parse(String[] arguments, Map<String, String> additionalArgs) {
String serviceName = "TestVote";
String username = null;
String address = "127.0.0.1";
long localTimestamp = System.currentTimeMillis();
String timestamp = Long.toString(localTimestamp, 10);
for (String s : arguments) {
Matcher m = argumentPattern.matcher(s);
if (m.matches()) {
String key = m.group(1).toLowerCase();
String v = m.group(2);
if (key.equals("servicename")) {
serviceName = v;
} else if (key.equals("username")) {
if (v.length() > 16)
throw new IllegalArgumentException("Illegal username - must be less than 16 characters long.");
username = v;
} else if (key.equals("address")) {
address = v;
} else if (key.equals("timestamp")) {
timestamp = v;
} else {
if (additionalArgs != null)
additionalArgs.put(key, v);
}
} else {
if (s.length() > 16)
throw new IllegalArgumentException("Illegal username - must be less than 16 characters long.");
username = s; // ezpz
}
}
if (username == null)
throw new IllegalArgumentException("Username not specified!");
return new Vote(serviceName, username, address, timestamp);
}
}
@@ -1,11 +0,0 @@
package com.vexsoftware.votifier.util;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
public class KeyCreator {
public static Key createKeyFrom(String token) {
return new SecretKeySpec(token.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
}
}
@@ -1,101 +0,0 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierSession;
import io.ibj.nuvotifier.platform.platform.LoggingAdapter;
import io.ibj.nuvotifier.platform.platform.VotifierPlugin;
import io.ibj.nuvotifier.platform.platform.scheduler.VotifierScheduler;
import com.vexsoftware.votifier.util.KeyCreator;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class TestVotifierPlugin implements VotifierPlugin {
private static final byte[] PUBLIC_KEY;
private static final byte[] PRIVATE_KEY;
static byte[] r(String u) throws Exception {
URL resourceUrl = TestVotifierPlugin.class.getResource(u);
Path resourcePath = Paths.get(resourceUrl.toURI());
return Base64.getDecoder().decode(new String(Files.readAllBytes(resourcePath), StandardCharsets.UTF_8));
}
static {
try {
PUBLIC_KEY = r("/public.key");
PRIVATE_KEY = r("/private.key");
} catch (Exception e) {
throw new AssertionError(e);
}
}
public static final TestVotifierPlugin I = new TestVotifierPlugin();
public static TestVotifierPlugin getI() {
return I;
}
private final Map<String, Key> keyMap = new HashMap<>();
private final KeyPair keyPair;
public TestVotifierPlugin() {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
PUBLIC_KEY);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
PRIVATE_KEY);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
keyPair = new KeyPair(publicKey, privateKey);
} catch (Exception e) {
throw new AssertionError(e);
}
keyMap.put("default", KeyCreator.createKeyFrom("test"));
}
@Override
public Map<String, Key> getTokens() {
return keyMap;
}
@Override
public KeyPair getProtocolV1Key() {
return keyPair;
}
@Override
public LoggingAdapter getPluginLogger() {
return null;
}
@Override
public VotifierScheduler getScheduler() {
return null;
}
public void specificKeysOnly() {
keyMap.clear();
keyMap.put("Test", KeyCreator.createKeyFrom("test"));
}
public void restoreDefault() {
keyMap.clear();
keyMap.put("default", KeyCreator.createKeyFrom("test"));
}
@Override
public void onVoteReceived(Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) throws Exception {
}
}
@@ -1,31 +0,0 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.model.Vote;
import io.ibj.nuvotifier.legacyserver.protocol.v1crypto.RSA;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
public class VoteUtil {
public static byte[] encodePOJOv1(Vote vote) throws Exception {
return encodePOJOv1(vote, TestVotifierPlugin.getI().getProtocolV1Key().getPublic());
}
public static byte[] encodePOJOv1(Vote vote, PublicKey key) throws Exception {
List<String> ordered = new ArrayList<>();
ordered.add("VOTE");
ordered.add(vote.getServiceName());
ordered.add(vote.getUsername());
ordered.add(vote.getAddress());
ordered.add(vote.getTimeStamp());
StringBuilder builder = new StringBuilder();
for (String s : ordered) {
builder.append(s).append('\n'); // naive join needed!
}
return RSA.encrypt(builder.toString().getBytes(StandardCharsets.UTF_8), key);
}
}
@@ -1,84 +0,0 @@
package com.vexsoftware.votifier.net.protocol;
import io.ibj.nuvotifier.legacyserver.protocol.VotifierProtocol1Decoder;
import io.ibj.nuvotifier.platform.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierSession;
import io.ibj.nuvotifier.legacyserver.protocol.v1crypto.RSA;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.CorruptedFrameException;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import static org.junit.jupiter.api.Assertions.*;
public class VotifierProtocol1DecoderTest {
private static final VotifierSession SESSION = new VotifierSession();
private EmbeddedChannel createChannel() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocol1Decoder());
channel.attr(VotifierSession.KEY).set(SESSION);
channel.attr(VotifierPlugin.KEY).set(TestVotifierPlugin.getI());
return channel;
}
@Test
public void testSuccessfulDecode() throws Exception {
Vote votePojo = new Vote("Test", "test", "test", "test");
// Send the vote
EmbeddedChannel channel = createChannel();
byte[] encrypted = VoteUtil.encodePOJOv1(votePojo);
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
assertTrue(channel.writeInbound(encryptedByteBuf));
Object presumedVote = channel.readInbound();
assertEquals(votePojo, presumedVote);
assertFalse(channel.finish());
}
private void verifyFailure(String bad) throws Exception {
// Send the bad vote
EmbeddedChannel channel = createChannel();
byte[] encrypted = RSA.encrypt(bad.getBytes(StandardCharsets.UTF_8), TestVotifierPlugin.getI().getProtocolV1Key().getPublic());
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
assertThrows(CorruptedFrameException.class, () -> channel.writeInbound(encryptedByteBuf));
channel.close();
}
@Test
public void testFailureDecodeMissingField() throws Exception {
verifyFailure("VOTE\nTest\ntest\ntest"); // missing field
}
@Test
public void testFailureDecodeBadOpcode() throws Exception {
verifyFailure("TEST\nTest\ntest\ntest\ntest\n");
}
@Test
public void testFailureDecodeBadRsa() throws Exception {
// Decode our bad RSA key
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(TestVotifierPlugin.r("/bad_public.key"));
PublicKey badPublicKey = keyFactory.generatePublic(publicKeySpec);
// Send the bad vote
EmbeddedChannel channel = createChannel();
byte[] encrypted = VoteUtil.encodePOJOv1(new Vote("Test", "test", "test", "test"), badPublicKey);
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
assertThrows(CorruptedFrameException.class, ()->channel.writeInbound(encryptedByteBuf));
channel.close();
}
}
@@ -1,139 +0,0 @@
package com.vexsoftware.votifier.net.protocol;
import com.google.gson.JsonObject;
import io.ibj.nuvotifier.legacyserver.protocol.VotifierProtocol2Decoder;
import io.ibj.nuvotifier.platform.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierSession;
import com.vexsoftware.votifier.util.GsonInst;
import com.vexsoftware.votifier.util.KeyCreator;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.DecoderException;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import static org.junit.jupiter.api.Assertions.*;
public class VotifierProtocol2DecoderTest {
private static final VotifierSession SESSION = new VotifierSession();
private EmbeddedChannel createChannel() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocol2Decoder());
channel.attr(VotifierSession.KEY).set(SESSION);
channel.attr(VotifierPlugin.KEY).set(TestVotifierPlugin.getI());
return channel;
}
private void sendVote(Vote vote, Key key, boolean expectSuccess) throws Exception {
// Create a well-formed request
EmbeddedChannel channel = createChannel();
JSONObject object = new JSONObject();
JsonObject payload = vote.serialize();
payload.addProperty("challenge", SESSION.getChallenge());
String payloadEncoded = GsonInst.gson.toJson(payload);
object.put("payload", payloadEncoded);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
object.put("signature",
Base64.getEncoder().encodeToString(mac.doFinal(payloadEncoded.getBytes(StandardCharsets.UTF_8))));
if (expectSuccess) {
assertTrue(channel.writeInbound(object.toString()));
assertEquals(vote, channel.readInbound());
assertFalse(channel.finish());
} else {
try {
channel.writeInbound(object.toString());
} finally {
channel.close();
}
}
}
@Test
public void testSuccessfulDecode() throws Exception {
sendVote(new Vote("Test", "test", "test", "0"), TestVotifierPlugin.getI().getTokens().get("default"), true);
}
@Test
public void testFailureDecodeBadPacket() {
// Create a well-formed request
EmbeddedChannel channel = createChannel();
Vote vote = new Vote("Test", "test", "test", "0");
JSONObject object = new JSONObject();
JsonObject payload = vote.serialize();
payload.addProperty("challenge", SESSION.getChallenge());
object.put("payload", GsonInst.gson.toJson(payload));
// We "forget" the signature.
assertThrows(DecoderException.class, () -> channel.writeInbound(object.toString()));
channel.close();
}
@Test
public void testFailureDecodeBadVoteField() throws Exception {
// Create a well-formed request
EmbeddedChannel channel = createChannel();
Vote vote = new Vote("Test", "test", "test", "0");
JSONObject object = new JSONObject();
JsonObject payload = vote.serialize();
String payloadEncoded = GsonInst.gson.toJson(payload);
// We "forget" the challenge.
object.put("payload", payloadEncoded);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(TestVotifierPlugin.getI().getTokens().get("default"));
object.put("signature",
Base64.getEncoder().encodeToString(mac.doFinal(payloadEncoded.getBytes(StandardCharsets.UTF_8))));
assertThrows(DecoderException.class, () -> channel.writeInbound(object.toString()));
channel.close();
}
@Test
public void testFailureDecodeBadChallenge() throws Exception {
// Create a well-formed request
EmbeddedChannel channel = createChannel();
Vote vote = new Vote("Test", "test", "test", "0");
JSONObject object = new JSONObject();
JsonObject payload = vote.serialize();
// We provide the wrong challenge.
payload.addProperty("challenge", "not a challenge for me");
object.put("payload", payload.toString());
String payloadEncoded = GsonInst.gson.toJson(payload);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(TestVotifierPlugin.getI().getTokens().get("default"));
object.put("signature",
Base64.getEncoder().encode(mac.doFinal(payloadEncoded.getBytes(StandardCharsets.UTF_8))));
assertThrows(DecoderException.class, () -> channel.writeInbound(object.toString()));
channel.close();
}
@Test
public void testFailureDecodeNonExistentKey() throws Exception {
TestVotifierPlugin.getI().specificKeysOnly();
Vote vote = new Vote("Bad Service", "test", "test", "0");
assertThrows(DecoderException.class, () -> sendVote(vote, TestVotifierPlugin.getI().getTokens().get("Test"), false));
TestVotifierPlugin.getI().restoreDefault();
}
@Test
public void testFailureDecodeBadSignature() {
Vote vote = new Vote("Bad Service", "test", "test", "0");
assertThrows(CorruptedFrameException.class, () -> sendVote(vote, KeyCreator.createKeyFrom("BadKey"), false));
}
}
@@ -1,93 +0,0 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierSession;
import io.ibj.nuvotifier.legacyserver.protocol.VotifierProtocolDifferentiator;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.DecoderException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class VotifierProtocolDifferentiatorTest {
@Test
public void v1Test() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, true));
VotifierSession session = new VotifierSession();
channel.attr(VotifierSession.KEY).set(session);
ByteBuf test = Unpooled.buffer(256);
for (int i = 0; i < 256; i++) {
test.writeByte(0);
}
channel.writeInbound(test);
assertEquals(VotifierSession.ProtocolVersion.ONE, session.getVersion());
channel.close();
}
@Test
public void v2Test() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
VotifierSession session = new VotifierSession();
channel.attr(VotifierSession.KEY).set(session);
ByteBuf test = Unpooled.buffer();
test.writeShort(0x733A);
channel.writeInbound(test);
assertEquals(VotifierSession.ProtocolVersion.TWO, session.getVersion());
channel.close();
}
@Test
public void failIfv1NotSupported() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
VotifierSession session = new VotifierSession();
channel.attr(VotifierSession.KEY).set(session);
ByteBuf test = Unpooled.buffer(256);
for (int i = 0; i < 256; i++) {
test.writeByte(0);
}
assertThrows(CorruptedFrameException.class, () -> channel.writeInbound(test));
channel.close();
}
@Test
public void failOnBadPacketTest() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
ByteBuf buf = Unpooled.buffer();
for (int i = 0; i < 3; i++) {
buf.writeByte(0);
}
assertThrows(DecoderException.class, () -> channel.writeInbound(buf));
channel.close();
}
@Test
public void tryIdentifyRealVotev1() throws Exception {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, true));
VotifierSession session = new VotifierSession();
channel.attr(VotifierSession.KEY).set(session);
Vote votePojo = new Vote("Test", "test", "test", "test");
byte[] encrypted = VoteUtil.encodePOJOv1(votePojo);
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
channel.writeInbound(encryptedByteBuf);
assertEquals(VotifierSession.ProtocolVersion.ONE, session.getVersion());
channel.close();
}
}
@@ -1,47 +0,0 @@
package com.vexsoftware.votifier.support.forwarding;
import com.google.common.collect.Lists;
import com.vexsoftware.votifier.model.Vote;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
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")
);
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
View File
@@ -1 +0,0 @@
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArrOsFuGPoHDZEFRHXriZRqrUXXjUOhVabC0Xu+WPtVbQrvNngN0igo0ZJIZELflhsFNBUJAnREo+wColfDg1rk2juEUYDt+gRzrN3WiqgzZolF3E4Dg4LrXh6cPuu4AeOZW9CtjXt2JwPBhlWLZb2RFhA6FziFLeMBa42/5VLFCQAmiuuU3OCiBskR3Jz/E5zqc7V/j247OMm+WRJ/k8OFTEFB+zFINOZY1ud3ltwkzQIPi1luGNW8Yt8+CEg/6hDiVlpFwgsazt4uKI/UfU5oP0kAKjQUfFGvNaBtJ0RlsBeJ7VDpOfIG6dDb8pe4IaA9aJLVcXWEn30pHsyYl2NwIDAQAB
-1
View File
@@ -1 +0,0 @@
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCd5osoNvoLM52pN8U15QkRXpGVxk0WaoNLWgN24pnW30FIAO3bp8qD2FXJzX2hvw43LIwudVEbwmdzeytVWDNuigLqEsJJsXCHh9ORKjGVHvS240r6idfxJ139Jz2d3oSZG8acHdHgs/H/M98O8lz9Zb2KHtm+rxpiwsV2SdhMtfMDJmRx4p0JklgJ8TV1ZApoypbGtRKXHOirVSs6zfgaYaeglUNITAgsZZNZk+j9Pp572FxyYryxkUaWVn09RAD07XvWKJc3f7NBElA2LpuZXTYJh3BXaI/N05tdgPej5o99PXaA2UMhdqo778zoQoeWzOvbNMUqZROqGW5WDwhdAgMBAAECggEAM8xcXB3k2uXc05UrCaoP1nXadnKt+SuJ1J87wbVGWDm21aMQZF1fzu6Z/fB+RSBCzpC7j8YGjH+ypiyCo6WigaGeoSD0QW6HBr7lBZr81vlnt9NKVnP/tel/KLvFt9M2Vm/JMI+LuRXuzhQ7GVEidjH5AqVqMuaIVlukoVBKN6d20KihcHeCjOZLAoL727QYGrHNr0eH9wQpgMzj3A51ubi5ma3bDvnSvN4ZoP3BhmDTRVnHEh4hx/XFYELi3RDGN2SDjegNXlZ+e03BHCGRryOCuwY1eua78gkrWN8sr3QAdWhEN/Pdwq0BpVsdsbyPzmgoamAMmBbKyBnEJxuvlQKBgQDtLQIhFVYA7vNtpwnXV3ahyEPCNf+tLJYsLYjKWRpoTw6bI3FfgMAEPGbDXLsmLi37mvRmCJpoU9j4nfRd/umoEIW6xuaYDdGRpyaiOlkEXyJd0n3uv/3mF7VtyltrPK3Mt2Gb7kGaZqe07B4a5UC02CCvpKqpbFfC0XxHP32GkwKBgQCqbs2D/pNcdA9iHm+AhX/oylK+BfiruTvLx/c2wOFleQOiNZUZCC6zELIDzuQXPlk9mlNshnciIItcjnyLGkDr2lvJpt6Bi65Jx5hYhlWrms883/hRUSjgKtJ9PFWl8wUVTJJsrOg0fm2ZM+YXpO9AvziRTEPj2MeibQjjlfsbTwKBgHc1ZBiUzMFzWM8IYh3mTC5MpiH7WiMEggZ6bIKn/mnnMoQvcFB8OgbnBQ+0kCxEkcAgxi5yrIk7e5aabbTA51gd1dlnF4fdijQXO4fWo99aob9eiQVzYn4/9GTWjNR9jy2SDnF9c2PTrP92njsOd1NuPh+KOG0nk4TMHhcBhmoZAoGAF4ZPy8ZZ5MsITIzS6zSCIUm7/gqo9lsNC+XAE8iv0+fOPpeSyu+8Mm89gxUNmr+6Zaxlk3dD/mvrkI0CV9j52RQ80yq7T7pOBlUMjDuOm680p7xqdB71u2ttvmeTkuJmczgshd+f9Y715c2qFt1nQNKkJjoWrN6BP8Q5E+Dh1jUCgYEApBuFGwmcSob6rIzinM4nJRyMLXqIy6nKCy8bBLkWCFPGztjIxhKBA/pErADhkLaLyM5reeMPNBbxruAFJ3mzfOneKf9lt0oHq2/jq0vJvYUj9tZjD2Sw6dhj0JqQhKIkhux1rU1xAAedY21JGTS+Be0dFfSv5uDrErPJiN+Qyt0=
-1
View File
@@ -1 +0,0 @@
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAneaLKDb6CzOdqTfFNeUJEV6RlcZNFmqDS1oDduKZ1t9BSADt26fKg9hVyc19ob8ONyyMLnVRG8Jnc3srVVgzbooC6hLCSbFwh4fTkSoxlR70tuNK+onX8Sdd/Sc9nd6EmRvGnB3R4LPx/zPfDvJc/WW9ih7Zvq8aYsLFdknYTLXzAyZkceKdCZJYCfE1dWQKaMqWxrUSlxzoq1UrOs34GmGnoJVDSEwILGWTWZPo/T6ee9hccmK8sZFGllZ9PUQA9O171iiXN3+zQRJQNi6bmV02CYdwV2iPzdObXYD3o+aPfT12gNlDIXaqO+/M6EKHlszr2zTFKmUTqhluVg8IXQIDAQAB
-1
View File
@@ -1 +0,0 @@
resources/
-22
View File
@@ -1,22 +0,0 @@
.PHONY: clean, all, run
all : resources/paper.jar resources/proxy.jar
resources :
mkdir resources
resources/paper.jar : resources
wget -O resources/paper.jar https://papermc.io/ci/job/Paper-1.13/lastSuccessfulBuild/artifact/paperclip.jar
resources/proxy.jar : resources
wget -O resources/proxy.jar https://papermc.io/ci/job/Waterfall/lastSuccessfulBuild/artifact/Waterfall-Proxy/bootstrap/target/Waterfall.jar
clean :
rm -r resources/
run :
tmux new-session './proxy/run.sh' \; \
split-window -h './s1/run.sh' \; \
split-window -h './s2/run.sh' \; \
select-layout even-horizontal
-26
View File
@@ -1,26 +0,0 @@
## Using this testbed
This testbed runs one Bungeecord as well as two Paper servers behind it, labeled
s1 and s2. Ports for where everything is bound is listed below.
First, download required sources using the following command:
```
make
```
Then, each time you want to launch a session, run:
```
make run
```
FYI, this only works in Linux afaik because thats what I do my development on.
# Ports
| Server | MC Port | Votifier Port |
| ------ | ------- | ------------- |
| proxy | 25577 | 8192 |
| s1 | 25565 | 8193 |
| s2 | 25566 | 8194 |
-4
View File
@@ -1,4 +0,0 @@
logs/
modules/
locations.yml
-48
View File
@@ -1,48 +0,0 @@
forge_support: true
timeout: 30000
online_mode: true
log_commands: false
listeners:
- query_port: 25577
motd: '&4its a bungee cord surbur'
tab_list: GLOBAL_PING
query_enabled: false
proxy_protocol: false
forced_hosts: {}
ping_passthrough: false
priorities:
- s1
bind_local_address: true
host: 0.0.0.0:25577
max_players: 1
tab_size: 60
force_default_server: false
connection_throttle: 1
groups:
md_5:
- admin
log_pings: true
ip_forward: true
prevent_proxy_connections: true
network_compression_threshold: 256
disabled_commands: []
stats: bf8a54ea-2917-4b9e-b463-2c05bd7f1ac6
permissions:
default:
- bungeecord.command.server
- bungeecord.command.list
admin:
- bungeecord.command.alert
- bungeecord.command.end
- bungeecord.command.ip
- bungeecord.command.reload
servers:
s1:
motd: SERVER 1
address: localhost:25565
restricted: false
s2:
motd: SERVER 2
address: localhost:25566
restricted: false
player_limit: -1
-8
View File
@@ -1,8 +0,0 @@
version: 2
modules:
- jenkins://cmd_alert
- jenkins://cmd_find
- jenkins://cmd_list
- jenkins://cmd_send
- jenkins://cmd_server
- jenkins://reconnect_yaml
-1
View File
@@ -1 +0,0 @@
rsa/
-74
View File
@@ -1,74 +0,0 @@
# The IP to listen to. Use 0.0.0.0 if you wish to listen to all interfaces on your server. (All IP addresses)
# This defaults to the IP you have configured your server to listen on, or 0.0.0.0 if you have not configured this.
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.
default: jqpli3gnd8l67qsg2d81vje09h
# 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.
disable-v1-protocol: false
# Configuration section for all vote forwarding to NuVotifier
forwarding:
# Sets whether to set up a remote method for fowarding. Supported methods:
# - none - Does not set up a forwarding method.
# - pluginMessaging - Sets up plugin messaging.
# - proxy - Proxies votes to other NuVotifier servers from this server.
method: none
pluginMessaging:
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 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.
# - 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
# Only sends the vote to the server in which the player is currently joined.
onlySendToJoinedServer: false
#Sets which server to send the vote in the event the player is not online when the vote is cast
#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
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:
address: 127.0.0.1
port: 8193
token: MyKeyHere
Hub2:
address: 127.0.0.1
port: 8194
token: AnotherKeyHere
-1
View File
@@ -1 +0,0 @@
../../../universal/build/libs/nuvotifier.jar
-1
View File
@@ -1 +0,0 @@
../resources/proxy.jar
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd $DIR
java -jar proxy.jar
-8
View File
@@ -1,8 +0,0 @@
use_netty_dns_resolver: true
disable_modern_tab_limiter: true
log_initial_handler_connections: true
throttling:
tab_complete: 1000
game_version: ''
log_server_list_ping: false
allow_empty_packets: false
-10
View File
@@ -1,10 +0,0 @@
#!/bin/sh
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd $DIR
echo <<WARNING
Hey! Just so you know, I am using the IReallyKnowWhatIAmDoingISwear flag, as
well as overriding the eula check. If you don't agree to the Mojang EULA
thingie, then uh, smash your computer or something because you totally agreed
to it so just fyi
WARNING
java -DIReallyKnowWhatIAmDoingISwear -Dcom.mojang.eula.agree=true -jar paper.jar
-19
View File
@@ -1,19 +0,0 @@
banned-ips.json
banned-players.json
cache/
eula.txt
logs/
permissions.yml
ops.json
usercache.json
version_history.json
whitelist.json
world/
world_nether/
world_the_end/
-37
View File
@@ -1,37 +0,0 @@
# This is the main configuration file for Bukkit.
# As you can see, there's actually not that much to configure without any plugins.
# For a reference for any variable inside this file, check out the Bukkit Wiki at
# https://www.spigotmc.org/go/bukkit-yml
#
# If you need help on this file, feel free to join us on irc or leave a message
# on the forums asking for advice.
#
# IRC: #spigot @ irc.spi.gt
# (If this means nothing to you, just go to https://www.spigotmc.org/go/irc )
# Forums: https://www.spigotmc.org/
# Bug tracker: https://www.spigotmc.org/go/bugs
settings:
allow-end: true
warn-on-overload: true
permissions-file: permissions.yml
update-folder: update
plugin-profiling: false
connection-throttle: 4000
query-plugins: true
deprecated-verbose: default
shutdown-message: Server closed
spawn-limits:
monsters: 70
animals: 10
water-animals: 15
ambient: 15
chunk-gc:
period-in-ticks: 600
load-threshold: 0
ticks-per:
animal-spawns: 400
monster-spawns: 1
autosave: 6000
aliases: now-in-commands.yml
-17
View File
@@ -1,17 +0,0 @@
# This is the commands configuration file for Bukkit.
# For documentation on how to make use of this file, check out the Bukkit Wiki at
# https://www.spigotmc.org/go/commands-yml
#
# If you need help on this file, feel free to join us on irc or leave a message
# on the forums asking for advice.
#
# IRC: #spigot @ irc.spi.gt
# (If this means nothing to you, just go to https://www.spigotmc.org/go/irc )
# Forums: https://www.spigotmc.org/
# Bug tracker: https://www.spigotmc.org/go/bugs
command-block-overrides: []
ignore-vanilla-permissions: false
aliases:
icanhasbukkit:
- version $1-
-56
View File
@@ -1,56 +0,0 @@
# This is the help configuration file for Bukkit.
#
# By default you do not need to modify this file. Help topics for all plugin commands are automatically provided by
# or extracted from your installed plugins. You only need to modify this file if you wish to add new help pages to
# your server or override the help pages of existing plugin commands.
#
# This file is divided up into the following parts:
# -- general-topics: lists admin defined help topics
# -- index-topics: lists admin defined index topics
# -- amend-topics: lists topic amendments to apply to existing help topics
# -- ignore-plugins: lists any plugins that should be excluded from help
#
# Examples are given below. When amending command topic, the string <text> will be replaced with the existing value
# in the help topic. Color codes can be used in topic text. The color code character is & followed by 0-F.
# ================================================================
#
# Set this to true to list the individual command help topics in the master help.
# command-topics-in-master-index: true
#
# Each general topic will show up as a separate topic in the help index along with all the plugin command topics.
# general-topics:
# Rules:
# shortText: Rules of the server
# fullText: |
# &61. Be kind to your fellow players.
# &B2. No griefing.
# &D3. No swearing.
# permission: topics.rules
#
# Each index topic will show up as a separate sub-index in the help index along with all the plugin command topics.
# To override the default help index (displayed when the user executes /help), name the index topic "Default".
# index-topics:
# Ban Commands:
# shortText: Player banning commands
# preamble: Moderator - do not abuse these commands
# permission: op
# commands:
# - /ban
# - /ban-ip
# - /banlist
#
# Topic amendments are used to change the content of automatically generated plugin command topics.
# amended-topics:
# /stop:
# shortText: Stops the server cold....in its tracks!
# fullText: <text> - This kills the server.
# permission: you.dont.have
#
# Any plugin in the ignored plugins list will be excluded from help. The name must match the name displayed by
# the /plugins command. Ignore "Bukkit" to remove the standard bukkit commands from the index. Ignore "All"
# to completely disable automatic help topic generation.
# ignore-plugins:
# - PluginNameOne
# - PluginNameTwo
# - PluginNameThree
-1
View File
@@ -1 +0,0 @@
../resources/paper.jar
-178
View File
@@ -1,178 +0,0 @@
# This is the main configuration file for Paper.
# As you can see, there's tons to configure. Some options may impact gameplay, so use
# with caution, and make sure you know what each option does before configuring.
#
# If you need help with the configuration or have any questions related to Paper,
# join us in our Discord or IRC channel.
#
# Discord: https://paperdiscord.emc.gs
# IRC: #paper @ irc.spi.gt ( http://irc.spi.gt/iris/?channels=paper )
# Website: https://papermc.io/
# Docs: https://paper.readthedocs.org/
verbose: false
config-version: 16
settings:
load-permissions-yml-before-plugins: true
bungee-online-mode: true
use-versioned-world: false
velocity-support:
enabled: false
online-mode: false
secret: ''
queue-light-updates-max-loss: 10
suggest-player-names-when-null-tab-completions: true
watchdog:
early-warning-every: 5000
early-warning-delay: 10000
spam-limiter:
tab-spam-increment: 1
tab-spam-limit: 500
save-player-data: true
sleep-between-chunk-saves: false
region-file-cache-size: 256
save-empty-scoreboard-teams: false
incoming-packet-spam-threshold: 300
book-size:
page-max: 2560
total-multiplier: 0.98
enable-player-collisions: true
player-auto-save-rate: -1
max-player-auto-save-per-tick: -1
async-chunks:
enable: true
generation: true
thread-per-world-generation: true
load-threads: -1
use-alternative-luck-formula: false
messages:
no-permission: '&cI''m sorry, but you do not have permission to perform this command.
Please contact the server administrators if you believe that this is in error.'
kick:
authentication-servers-down: ''
flying-player: Flying is not enabled on this server
flying-vehicle: Flying is not enabled on this server
connection-throttle: Connection throttled! Please wait before reconnecting.
timings:
enabled: true
verbose: true
server-name-privacy: false
hidden-config-entries:
- database
- settings.bungeecord-addresses
history-interval: 300
history-length: 3600
world-settings:
default:
portal-search-radius: 128
game-mechanics:
villages-load-chunks: false
scan-for-legacy-ender-dragon: true
disable-chest-cat-detection: false
disable-end-credits: false
disable-player-crits: false
disable-sprint-interruption-on-attack: false
shield-blocking-delay: 5
disable-unloaded-chunk-enderpearl-exploit: true
queue-light-updates: false
optimize-explosions: false
disable-teleportation-suffocation-check: false
fixed-chunk-inhabited-time: -1
use-vanilla-world-scoreboard-name-coloring: false
delay-chunk-unloads-by: 10s
max-auto-save-chunks-per-tick: 24
save-queue-limit-for-auto-save: 50
enable-treasure-maps: true
treasure-maps-return-already-discovered: false
experience-merge-max-value: -1
max-chunk-sends-per-tick: 81
max-chunk-gens-per-tick: 10
prevent-moving-into-unloaded-chunks: false
max-growth-height:
cactus: 3
reeds: 3
fishing-time-range:
MinimumTicks: 100
MaximumTicks: 600
despawn-ranges:
soft: 32
hard: 128
falling-block-height-nerf: 0
tnt-entity-height-nerf: 0
lightning-strike-distance-limit:
sound: -1
impact-sound: -1
flash: -1
frosted-ice:
enabled: true
delay:
min: 20
max: 40
lootables:
auto-replenish: false
restrict-player-reloot: true
reset-seed-on-fill: true
max-refills: -1
refresh-min: 12h
refresh-max: 2d
filter-nbt-data-from-spawn-eggs-and-related: true
max-entity-collisions: 8
disable-creeper-lingering-effect: false
hopper:
cooldown-when-full: true
disable-move-event: false
duplicate-uuid-resolver: saferegen
duplicate-uuid-saferegen-delete-range: 32
remove-corrupt-tile-entities: false
keep-spawn-loaded: true
anti-xray:
enabled: false
engine-mode: 1
chunk-edge-mode: 1
max-chunk-section-index: 3
update-radius: 2
hidden-blocks:
- gold_ore
- iron_ore
- coal_ore
- lapis_ore
- mossy_cobblestone
- obsidian
- chest
- diamond_ore
- redstone_ore
- lit_redstone_ore
- clay
- emerald_ore
- ender_chest
replacement-blocks:
- stone
- planks
skip-entity-ticking-in-chunks-scheduled-for-unload: true
armor-stands-do-collision-entity-lookups: true
keep-spawn-loaded-range: 8
auto-save-interval: -1
disable-thunder: false
skeleton-horse-thunder-spawn-chance: 0.01
disable-ice-and-snow: false
fire-physics-event-for-redstone: false
grass-spread-tick-rate: 1
water-over-lava-flow-speed: 5
bed-search-radius: 1
use-faster-eigencraft-redstone: false
nether-ceiling-void-damage: false
allow-non-player-entities-on-scoreboards: false
container-update-tick-rate: 1
prevent-tnt-from-moving-in-water: false
non-player-arrow-despawn-rate: -1
creative-arrow-despawn-rate: -1
parrots-are-unaffected-by-player-movement: false
disable-explosion-knockback: false
armor-stands-tick: true
spawner-nerfed-mobs-should-jump: false
allow-leashing-undead-horse: false
baby-zombie-movement-speed: 0.5
all-chunks-are-slime-chunks: false
mob-spawner-tick-rate: 1
squid-spawn-height:
maximum: 0.0
-1
View File
@@ -1 +0,0 @@
rsa/
-29
View File
@@ -1,29 +0,0 @@
# The IP to listen to. Use 0.0.0.0 if you wish to listen to all interfaces on your server. (All IP addresses)
# This defaults to the IP you have configured your server to listen on, or 0.0.0.0 if you have not configured this.
host: 0.0.0.0
# Port to listen for new votes on
port: 8193
# 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: true
# 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.
disable-v1-protocol: false
# All tokens, labeled by the serviceName of each server list.
tokens:
# Default token for all server lists, if another isn't supplied.
default: server1token
# Configuration section for all vote forwarding to NuVotifier
forwarding:
# Sets whether to set up a remote method for fowarding. Supported methods:
# - none - Does not set up a forwarding method.
# - pluginMessaging - Sets up plugin messaging
method: pluginMessaging
pluginMessaging:
channel: nuvotifier:votes
-7
View File
@@ -1,7 +0,0 @@
# bStats collects some data for plugin authors like how many servers are using their plugins.
# To honor their work, you should not disable it.
# This has nearly no effect on the server performance!
# Check out https://bStats.org/ to learn more :)
enabled: true
serverUuid: 44d2e780-6de3-4b18-a28c-745f58727b99
logFailedRequests: false
-1
View File
@@ -1 +0,0 @@
../s-run.sh
-39
View File
@@ -1,39 +0,0 @@
#Minecraft server properties
#Fri Dec 21 15:15:00 EST 2018
generator-settings=
force-gamemode=false
allow-nether=true
enforce-whitelist=false
gamemode=0
broadcast-console-to-ops=true
enable-query=false
player-idle-timeout=0
difficulty=1
spawn-monsters=true
op-permission-level=4
pvp=true
snooper-enabled=true
level-type=DEFAULT
hardcore=false
enable-command-block=false
max-players=20
network-compression-threshold=256
resource-pack-sha1=
max-world-size=29999984
server-port=25565
server-ip=
spawn-npcs=true
allow-flight=false
level-name=world
view-distance=10
resource-pack=
spawn-animals=true
white-list=false
generate-structures=true
online-mode=false
max-build-height=256
level-seed=
prevent-proxy-connections=false
use-native-transport=true
motd=A Minecraft Server
enable-rcon=false
-128
View File
@@ -1,128 +0,0 @@
# This is the main configuration file for Spigot.
# As you can see, there's tons to configure. Some options may impact gameplay, so use
# with caution, and make sure you know what each option does before configuring.
# For a reference for any variable inside this file, check out the Spigot wiki at
# http://www.spigotmc.org/wiki/spigot-configuration/
#
# If you need help with the configuration or have any questions related to Spigot,
# join us at the IRC or drop by our forums and leave a post.
#
# IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )
# Forums: http://www.spigotmc.org/
config-version: 11
settings:
debug: false
save-user-cache-on-stop-only: false
sample-count: 12
user-cache-size: 1000
moved-wrongly-threshold: 0.0625
moved-too-quickly-multiplier: 10.0
bungeecord: true
late-bind: false
player-shuffle: 0
timeout-time: 60
restart-on-crash: true
restart-script: ./start.sh
netty-threads: 4
attribute:
maxHealth:
max: 2048.0
movementSpeed:
max: 2048.0
attackDamage:
max: 2048.0
messages:
whitelist: You are not whitelisted on this server!
unknown-command: Unknown command. Type "/help" for help.
server-full: The server is full!
outdated-client: Outdated client! Please use {0}
outdated-server: Outdated server! I'm still on {0}
restart: Server is restarting
stats:
disable-saving: false
forced-stats: {}
commands:
tab-complete: 0
send-namespaced: true
log: true
spam-exclusions:
- /skill
silent-commandblock-console: false
replace-commands:
- setblock
- summon
- testforblock
- tellraw
advancements:
disable-saving: false
disabled:
- minecraft:story/disabled
world-settings:
default:
verbose: true
merge-radius:
exp: 3.0
item: 2.5
random-light-updates: false
dragon-death-sound-radius: 0
seed-village: 10387312
seed-desert: 14357617
seed-igloo: 14357618
seed-jungle: 14357619
seed-swamp: 14357620
seed-monument: 10387313
seed-shipwreck: 165745295
seed-ocean: 14357621
seed-slime: 987234911
hunger:
jump-walk-exhaustion: 0.05
jump-sprint-exhaustion: 0.2
combat-exhaustion: 0.1
regen-exhaustion: 6.0
swim-multiplier: 0.01
sprint-multiplier: 0.1
other-multiplier: 0.0
max-tnt-per-tick: 100
max-tick-time:
tile: 50
entity: 50
squid-spawn-range:
min: 45.0
mob-spawn-range: 8
growth:
cactus-modifier: 100
cane-modifier: 100
melon-modifier: 100
mushroom-modifier: 100
pumpkin-modifier: 100
sapling-modifier: 100
wheat-modifier: 100
netherwart-modifier: 100
vine-modifier: 100
cocoa-modifier: 100
entity-activation-range:
animals: 32
monsters: 32
misc: 16
water: 16
tick-inactive-villagers: true
entity-tracking-range:
players: 48
animals: 48
monsters: 48
misc: 32
other: 64
ticks-per:
hopper-transfer: 8
hopper-check: 1
hopper-amount: 1
wither-spawn-sound-radius: 0
enable-zombie-pigmen-portal-spawns: true
item-despawn-rate: 6000
view-distance: 10
arrow-despawn-rate: 1200
hanging-tick-frequency: 100
zombie-aggressive-towards-villager: true
nerf-spawner-mobs: false
max-entity-collisions: 8
-19
View File
@@ -1,19 +0,0 @@
banned-ips.json
banned-players.json
cache/
eula.txt
logs/
permissions.yml
ops.json
usercache.json
version_history.json
whitelist.json
world/
world_nether/
world_the_end/
-37
View File
@@ -1,37 +0,0 @@
# This is the main configuration file for Bukkit.
# As you can see, there's actually not that much to configure without any plugins.
# For a reference for any variable inside this file, check out the Bukkit Wiki at
# https://www.spigotmc.org/go/bukkit-yml
#
# If you need help on this file, feel free to join us on irc or leave a message
# on the forums asking for advice.
#
# IRC: #spigot @ irc.spi.gt
# (If this means nothing to you, just go to https://www.spigotmc.org/go/irc )
# Forums: https://www.spigotmc.org/
# Bug tracker: https://www.spigotmc.org/go/bugs
settings:
allow-end: true
warn-on-overload: true
permissions-file: permissions.yml
update-folder: update
plugin-profiling: false
connection-throttle: 4000
query-plugins: true
deprecated-verbose: default
shutdown-message: Server closed
spawn-limits:
monsters: 70
animals: 10
water-animals: 15
ambient: 15
chunk-gc:
period-in-ticks: 600
load-threshold: 0
ticks-per:
animal-spawns: 400
monster-spawns: 1
autosave: 6000
aliases: now-in-commands.yml
-17
View File
@@ -1,17 +0,0 @@
# This is the commands configuration file for Bukkit.
# For documentation on how to make use of this file, check out the Bukkit Wiki at
# https://www.spigotmc.org/go/commands-yml
#
# If you need help on this file, feel free to join us on irc or leave a message
# on the forums asking for advice.
#
# IRC: #spigot @ irc.spi.gt
# (If this means nothing to you, just go to https://www.spigotmc.org/go/irc )
# Forums: https://www.spigotmc.org/
# Bug tracker: https://www.spigotmc.org/go/bugs
command-block-overrides: []
ignore-vanilla-permissions: false
aliases:
icanhasbukkit:
- version $1-
-56
View File
@@ -1,56 +0,0 @@
# This is the help configuration file for Bukkit.
#
# By default you do not need to modify this file. Help topics for all plugin commands are automatically provided by
# or extracted from your installed plugins. You only need to modify this file if you wish to add new help pages to
# your server or override the help pages of existing plugin commands.
#
# This file is divided up into the following parts:
# -- general-topics: lists admin defined help topics
# -- index-topics: lists admin defined index topics
# -- amend-topics: lists topic amendments to apply to existing help topics
# -- ignore-plugins: lists any plugins that should be excluded from help
#
# Examples are given below. When amending command topic, the string <text> will be replaced with the existing value
# in the help topic. Color codes can be used in topic text. The color code character is & followed by 0-F.
# ================================================================
#
# Set this to true to list the individual command help topics in the master help.
# command-topics-in-master-index: true
#
# Each general topic will show up as a separate topic in the help index along with all the plugin command topics.
# general-topics:
# Rules:
# shortText: Rules of the server
# fullText: |
# &61. Be kind to your fellow players.
# &B2. No griefing.
# &D3. No swearing.
# permission: topics.rules
#
# Each index topic will show up as a separate sub-index in the help index along with all the plugin command topics.
# To override the default help index (displayed when the user executes /help), name the index topic "Default".
# index-topics:
# Ban Commands:
# shortText: Player banning commands
# preamble: Moderator - do not abuse these commands
# permission: op
# commands:
# - /ban
# - /ban-ip
# - /banlist
#
# Topic amendments are used to change the content of automatically generated plugin command topics.
# amended-topics:
# /stop:
# shortText: Stops the server cold....in its tracks!
# fullText: <text> - This kills the server.
# permission: you.dont.have
#
# Any plugin in the ignored plugins list will be excluded from help. The name must match the name displayed by
# the /plugins command. Ignore "Bukkit" to remove the standard bukkit commands from the index. Ignore "All"
# to completely disable automatic help topic generation.
# ignore-plugins:
# - PluginNameOne
# - PluginNameTwo
# - PluginNameThree
-1
View File
@@ -1 +0,0 @@
../resources/paper.jar
-178
View File
@@ -1,178 +0,0 @@
# This is the main configuration file for Paper.
# As you can see, there's tons to configure. Some options may impact gameplay, so use
# with caution, and make sure you know what each option does before configuring.
#
# If you need help with the configuration or have any questions related to Paper,
# join us in our Discord or IRC channel.
#
# Discord: https://paperdiscord.emc.gs
# IRC: #paper @ irc.spi.gt ( http://irc.spi.gt/iris/?channels=paper )
# Website: https://papermc.io/
# Docs: https://paper.readthedocs.org/
verbose: false
config-version: 16
settings:
load-permissions-yml-before-plugins: true
bungee-online-mode: true
incoming-packet-spam-threshold: 300
use-versioned-world: false
velocity-support:
enabled: false
online-mode: false
secret: ''
queue-light-updates-max-loss: 10
suggest-player-names-when-null-tab-completions: true
watchdog:
early-warning-every: 5000
early-warning-delay: 10000
spam-limiter:
tab-spam-increment: 1
tab-spam-limit: 500
save-player-data: true
sleep-between-chunk-saves: false
region-file-cache-size: 256
save-empty-scoreboard-teams: false
book-size:
page-max: 2560
total-multiplier: 0.98
enable-player-collisions: true
player-auto-save-rate: -1
max-player-auto-save-per-tick: -1
async-chunks:
enable: true
generation: true
thread-per-world-generation: true
load-threads: -1
use-alternative-luck-formula: false
messages:
no-permission: '&cI''m sorry, but you do not have permission to perform this command.
Please contact the server administrators if you believe that this is in error.'
kick:
authentication-servers-down: ''
connection-throttle: Connection throttled! Please wait before reconnecting.
flying-player: Flying is not enabled on this server
flying-vehicle: Flying is not enabled on this server
timings:
enabled: true
verbose: true
server-name-privacy: false
hidden-config-entries:
- database
- settings.bungeecord-addresses
history-interval: 300
history-length: 3600
world-settings:
default:
portal-search-radius: 128
game-mechanics:
villages-load-chunks: false
scan-for-legacy-ender-dragon: true
disable-chest-cat-detection: false
disable-end-credits: false
disable-player-crits: false
disable-sprint-interruption-on-attack: false
shield-blocking-delay: 5
disable-unloaded-chunk-enderpearl-exploit: true
queue-light-updates: false
optimize-explosions: false
disable-teleportation-suffocation-check: false
fixed-chunk-inhabited-time: -1
use-vanilla-world-scoreboard-name-coloring: false
delay-chunk-unloads-by: 10s
max-auto-save-chunks-per-tick: 24
save-queue-limit-for-auto-save: 50
enable-treasure-maps: true
treasure-maps-return-already-discovered: false
experience-merge-max-value: -1
max-chunk-sends-per-tick: 81
max-chunk-gens-per-tick: 10
prevent-moving-into-unloaded-chunks: false
max-growth-height:
cactus: 3
reeds: 3
fishing-time-range:
MinimumTicks: 100
MaximumTicks: 600
despawn-ranges:
soft: 32
hard: 128
falling-block-height-nerf: 0
tnt-entity-height-nerf: 0
remove-corrupt-tile-entities: false
lightning-strike-distance-limit:
sound: -1
impact-sound: -1
flash: -1
frosted-ice:
enabled: true
delay:
min: 20
max: 40
lootables:
auto-replenish: false
restrict-player-reloot: true
reset-seed-on-fill: true
max-refills: -1
refresh-min: 12h
refresh-max: 2d
filter-nbt-data-from-spawn-eggs-and-related: true
max-entity-collisions: 8
disable-creeper-lingering-effect: false
hopper:
cooldown-when-full: true
disable-move-event: false
duplicate-uuid-resolver: saferegen
duplicate-uuid-saferegen-delete-range: 32
keep-spawn-loaded: true
anti-xray:
enabled: false
engine-mode: 1
chunk-edge-mode: 1
max-chunk-section-index: 3
update-radius: 2
hidden-blocks:
- gold_ore
- iron_ore
- coal_ore
- lapis_ore
- mossy_cobblestone
- obsidian
- chest
- diamond_ore
- redstone_ore
- lit_redstone_ore
- clay
- emerald_ore
- ender_chest
replacement-blocks:
- stone
- planks
skip-entity-ticking-in-chunks-scheduled-for-unload: true
armor-stands-do-collision-entity-lookups: true
keep-spawn-loaded-range: 8
auto-save-interval: -1
disable-thunder: false
skeleton-horse-thunder-spawn-chance: 0.01
disable-ice-and-snow: false
fire-physics-event-for-redstone: false
grass-spread-tick-rate: 1
water-over-lava-flow-speed: 5
bed-search-radius: 1
use-faster-eigencraft-redstone: false
nether-ceiling-void-damage: false
allow-non-player-entities-on-scoreboards: false
container-update-tick-rate: 1
parrots-are-unaffected-by-player-movement: false
prevent-tnt-from-moving-in-water: false
non-player-arrow-despawn-rate: -1
creative-arrow-despawn-rate: -1
disable-explosion-knockback: false
armor-stands-tick: true
spawner-nerfed-mobs-should-jump: false
allow-leashing-undead-horse: false
baby-zombie-movement-speed: 0.5
all-chunks-are-slime-chunks: false
mob-spawner-tick-rate: 1
squid-spawn-height:
maximum: 0.0
-1
View File
@@ -1 +0,0 @@
rsa/
-29
View File
@@ -1,29 +0,0 @@
# The IP to listen to. Use 0.0.0.0 if you wish to listen to all interfaces on your server. (All IP addresses)
# This defaults to the IP you have configured your server to listen on, or 0.0.0.0 if you have not configured this.
host: 0.0.0.0
# Port to listen for new votes on
port: 8194
# 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: true
# 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.
disable-v1-protocol: false
# All tokens, labeled by the serviceName of each server list.
tokens:
# Default token for all server lists, if another isn't supplied.
default: server2token
# Configuration section for all vote forwarding to NuVotifier
forwarding:
# Sets whether to set up a remote method for fowarding. Supported methods:
# - none - Does not set up a forwarding method.
# - pluginMessaging - Sets up plugin messaging
method: pluginMessaging
pluginMessaging:
channel: nuvotifier:votes
-7
View File
@@ -1,7 +0,0 @@
# bStats collects some data for plugin authors like how many servers are using their plugins.
# To honor their work, you should not disable it.
# This has nearly no effect on the server performance!
# Check out https://bStats.org/ to learn more :)
enabled: true
serverUuid: ef382803-14ce-4a3a-82a9-3611593c622d
logFailedRequests: false
-1
View File
@@ -1 +0,0 @@
../../../universal/build/libs/nuvotifier.jar
-1
View File
@@ -1 +0,0 @@
../s-run.sh
-39
View File
@@ -1,39 +0,0 @@
#Minecraft server properties
#Fri Dec 21 15:15:00 EST 2018
generator-settings=
force-gamemode=false
allow-nether=true
enforce-whitelist=false
gamemode=0
broadcast-console-to-ops=true
enable-query=false
player-idle-timeout=0
difficulty=1
spawn-monsters=true
op-permission-level=4
pvp=true
snooper-enabled=true
level-type=DEFAULT
hardcore=false
enable-command-block=false
max-players=20
network-compression-threshold=256
resource-pack-sha1=
max-world-size=29999984
server-port=25566
server-ip=
spawn-npcs=true
allow-flight=false
level-name=world
view-distance=10
resource-pack=
spawn-animals=true
white-list=false
generate-structures=true
online-mode=false
max-build-height=256
level-seed=
prevent-proxy-connections=false
use-native-transport=true
motd=A Minecraft Server
enable-rcon=false
-128
View File
@@ -1,128 +0,0 @@
# This is the main configuration file for Spigot.
# As you can see, there's tons to configure. Some options may impact gameplay, so use
# with caution, and make sure you know what each option does before configuring.
# For a reference for any variable inside this file, check out the Spigot wiki at
# http://www.spigotmc.org/wiki/spigot-configuration/
#
# If you need help with the configuration or have any questions related to Spigot,
# join us at the IRC or drop by our forums and leave a post.
#
# IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )
# Forums: http://www.spigotmc.org/
config-version: 11
settings:
debug: false
save-user-cache-on-stop-only: false
sample-count: 12
timeout-time: 60
restart-on-crash: true
restart-script: ./start.sh
netty-threads: 4
attribute:
maxHealth:
max: 2048.0
movementSpeed:
max: 2048.0
attackDamage:
max: 2048.0
bungeecord: true
late-bind: false
player-shuffle: 0
user-cache-size: 1000
moved-wrongly-threshold: 0.0625
moved-too-quickly-multiplier: 10.0
messages:
whitelist: You are not whitelisted on this server!
unknown-command: Unknown command. Type "/help" for help.
server-full: The server is full!
outdated-client: Outdated client! Please use {0}
outdated-server: Outdated server! I'm still on {0}
restart: Server is restarting
stats:
disable-saving: false
forced-stats: {}
commands:
tab-complete: 0
send-namespaced: true
log: true
spam-exclusions:
- /skill
silent-commandblock-console: false
replace-commands:
- setblock
- summon
- testforblock
- tellraw
advancements:
disable-saving: false
disabled:
- minecraft:story/disabled
world-settings:
default:
verbose: true
random-light-updates: false
dragon-death-sound-radius: 0
seed-village: 10387312
seed-desert: 14357617
seed-igloo: 14357618
seed-jungle: 14357619
seed-swamp: 14357620
seed-monument: 10387313
seed-shipwreck: 165745295
seed-ocean: 14357621
seed-slime: 987234911
hunger:
jump-walk-exhaustion: 0.05
jump-sprint-exhaustion: 0.2
combat-exhaustion: 0.1
regen-exhaustion: 6.0
swim-multiplier: 0.01
sprint-multiplier: 0.1
other-multiplier: 0.0
max-tnt-per-tick: 100
max-tick-time:
tile: 50
entity: 50
squid-spawn-range:
min: 45.0
merge-radius:
exp: 3.0
item: 2.5
mob-spawn-range: 8
growth:
cactus-modifier: 100
cane-modifier: 100
melon-modifier: 100
mushroom-modifier: 100
pumpkin-modifier: 100
sapling-modifier: 100
wheat-modifier: 100
netherwart-modifier: 100
vine-modifier: 100
cocoa-modifier: 100
entity-activation-range:
animals: 32
monsters: 32
misc: 16
water: 16
tick-inactive-villagers: true
entity-tracking-range:
players: 48
animals: 48
monsters: 48
misc: 32
other: 64
ticks-per:
hopper-transfer: 8
hopper-check: 1
hopper-amount: 1
wither-spawn-sound-radius: 0
enable-zombie-pigmen-portal-spawns: true
item-despawn-rate: 6000
view-distance: 10
arrow-despawn-rate: 1200
zombie-aggressive-towards-villager: true
hanging-tick-frequency: 100
nerf-spawner-mobs: false
max-entity-collisions: 8
-10
View File
@@ -1,10 +0,0 @@
# Changing the version of NuVotifier
So since I keep forgetting, here are the places which need to have their version
numbers changed:
+ build.gradle
+ com.vexsoftware.votifier.sponge.VotifierPlugin @Plugin
+ com.vexsoftware.votifier.velocity.VotifierPlugin @Plugin
This is really annoying.
-80
View File
@@ -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>ibj-public</id>
<name>Ichbinjoe Public Repository</name>
<url>https://nexus.ibj.io/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://nexus.ibj.io/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)
-109
View File
@@ -1,109 +0,0 @@
# FAQ
Also know as, the page you go to when NuVotifier isn't doing what it is supposed to.
From maintaining NuVotifier for some time, I receive a lot of the same
questions. I try to answer them the best I can in this document.
## I voted (or submitted a test vote) and nothing happened. Why isn't it working?
Votifier v1 isn't smart enough to recover from errors. Most server list votifier
implementations are even stupid enough to _ignore_ connection errors and report
the vote as a success. Thus when using Votifier v1, there is no way to be sure
that a vote is ever received by the receiving server.
Usually when NuVotifier doesn't report anything in the logs, the voting website
is unable to connect to NuVotifier in the first place. The following things are
usually to blame:
+ NuVotifier errored out during startup - check your startup logs
+ NuVotifier couldn't bind to the ip/port combination - again, check your start
up logs. I have another section on NuVotifier failing to bind errors.
+ NuVotifier is not bound to the correct IP - Check the bind ip parameter in the
NuVotifier configuration. If that doesn't work, try `0.0.0.0` - this will tell
NuVotifier to bind to ALL IPs it can.
+ You entered the port in incorrectly - double check the NuVotifier port in the
configuration.
+ You have a firewall in the way - If you are running on Windows, try to
temporarily disable the firewall. Linux has a couple of different of culprits,
including `iptables` and `ufw`.
## I voted (or submitted a test vote) and I got an IOException in the console. What gives?
How to treat an IOException depends on which specific IOException you receive:
### Connection reset by peer
This means that the voting website 'closed' the TCP connection to NuVotifier
before it could read the entire Vote message. NuVotifier _can't_ fix this,
because this is an implementation issue with voting lists. If you are receiving
this exception, report this to voting lists, not NuVotifier.
If you are coming from a server list from a report of this error, consider
implementing the Votifier v2 protocol - it is more secure, easier to implement,
easier to diagnose, and is more resilient to network issues like this. Some
implementations for popular languages already exist including PHP, Node.js,
Golang, and Java.
### Connection reset by host
This means that your computer's operating system closed the TCP connection to
NuVotifier before it could read the entire Vote message. Usually this is due to
a firewall - disable firewalls on your system and try again.
### Other IOExceptions
Other errors don't show up commonly enough - report an issue with your
configurations and server logs.
## Which forwarding mode should I use?
NuVotifier comes with two baked in forwarding modes: proxy and pluginMessaging.
Proxy mode should only ever be used when your network includes multiple
BungeeCords. Otherwise, you should always use pluginMessaging - it is easier to
set up and includes more forwarding mechanics.
## Bungeecord gets my vote, however my backend servers don't.
First, make sure you are using the correct forwarding mode for your setup - you
could save yourself a lot of headaches by using pluginMessaging instead of proxy
mode. If you are still having issues, see the following sections.
### PluginMessaging
PluginMessaging is a lot more straight forward than other forwarding methods. If
it does not work, check the following things:
+ Is the plugin messaging channel the same everywhere? Make sure it is lower
case and includes a colon! (Previous NuVotifier versions defaulted to an
invalid channel before 1.13 was released.)
+ Is the server you are having issues with either on the blacklist or not on the
whitelist?
### Proxy
A lot more can go wrong when using proxy. A lot of people do not set it up
correctly. If you are using proxy mode, make absolutely sure you actually need
proxy mode. With that being said, here are some things you can check:
+ For each backend server, diagnose connection issues according to simply not
getting votes - usually the server is unable to bind to a port, or there is
another issue related to firewalls, ports, and that sort of thing.
+ Ensure you are using the v2 Votifier token generated by each server. This is
NOT the key found in the `rsa/` folder.
+ Check ALL of your logs. One of them says something. It doesn't? You are lying.
ONE OF THEM DOES. Check them again.
## Does NuVotifier support X?
The answer is yes if the original Votifier supported it or if the plugin says
they support NuVotifier.
## I used /reload or /plugman on NuVotifier and now it doesn't work!
Reloads are not supported by NuVotifier. Restart your server, and use
`/nvreload` in the console to reload the NuVotifier configuration.
## NuVotifier is lagging my server!
No its not. One of your vote listeners is.
-20
View File
@@ -1,20 +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).
# FAQ
You can find the answers to those common questions you have [here](faq.md).
-78
View File
@@ -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.
-244
View File
@@ -1,244 +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 `cacheTime` field. This number can be as high or low
as you want. It is measured in days within the cache. This is valid for both the
`file` and `memory` cache.
Sometimes, caches can grow quite large and try to dump lots of votes back over the
player's game connection. This is a problem, as the Bukkit server may think it is
instead the player spamming packets! NuVotifier rate-limits the votes which are
evicted per second over a plugin messaging channel using the `dumpRate` parameter.
### 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.
+16
View File
@@ -0,0 +1,16 @@
plugins {
id 'java'
}
group 'io.ibj.nuvotifier'
version '3.0.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
-3
View File
@@ -1,3 +0,0 @@
dependencies {
implementation group: "io.netty", name: "netty-handler", version: "4.1.32.Final"
}
+10 -8
View File
@@ -5,13 +5,15 @@ include 'model'
// The 'platform' api.
include 'platform'
include 'legacyserver'
include 'bukkit'
include 'bungeecord'
include 'common'
include 'sponge'
include 'universal'
include 'velocity'
include 'multiplatform'
// Servers
include 'legacyserver'
// Apis
include 'bukkit-api'
include 'bungeecord-api'
include 'sponge-api'
include 'velocity-api'
include 'engine'
+4
View File
@@ -0,0 +1,4 @@
dependencies {
api group: "org.spongepowered", name:"spongeapi", version: "7.1.0"
implementation project(":model")
}
-5
View File
@@ -1,5 +0,0 @@
dependencies {
compileOnly group: "org.spongepowered", name: "spongeapi", version: "7.1.0"
annotationProcessor group: "org.spongepowered", name: "spongeapi", version: "7.1.0"
compile project(":common")
}
@@ -1,42 +0,0 @@
package com.vexsoftware.votifier.sponge;
import io.ibj.nuvotifier.platform.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);
}
}
@@ -1,58 +0,0 @@
package com.vexsoftware.votifier.sponge;
import io.ibj.nuvotifier.platform.platform.scheduler.ScheduledVotifierTask;
import io.ibj.nuvotifier.platform.platform.scheduler.VotifierScheduler;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.scheduler.Task;
import java.util.concurrent.TimeUnit;
class SpongeScheduler implements VotifierScheduler {
private final VotifierPlugin plugin;
SpongeScheduler(VotifierPlugin plugin) {
this.plugin = plugin;
}
private Task.Builder taskBuilder(Runnable runnable) {
return Sponge.getScheduler().createTaskBuilder().execute(runnable);
}
@Override
public ScheduledVotifierTask sync(Runnable runnable) {
return new TaskWrapper(taskBuilder(runnable).submit(plugin));
}
@Override
public ScheduledVotifierTask onPool(Runnable runnable) {
return new TaskWrapper(taskBuilder(runnable).async().submit(plugin));
}
@Override
public ScheduledVotifierTask delayedSync(Runnable runnable, int delay, TimeUnit unit) {
return new TaskWrapper(taskBuilder(runnable).delay(delay, unit).submit(plugin));
}
@Override
public ScheduledVotifierTask delayedOnPool(Runnable runnable, int delay, TimeUnit unit) {
return new TaskWrapper(taskBuilder(runnable).delay(delay, unit).async().submit(plugin));
}
@Override
public ScheduledVotifierTask repeatOnPool(Runnable runnable, int delay, int repeat, TimeUnit unit) {
return new TaskWrapper(taskBuilder(runnable).delay(delay, unit).interval(repeat, unit).submit(plugin));
}
private static class TaskWrapper implements ScheduledVotifierTask {
private final Task task;
private TaskWrapper(Task task) {
this.task = task;
}
@Override
public void cancel() {
task.cancel();
}
}
}
@@ -1,299 +0,0 @@
package com.vexsoftware.votifier.sponge;
import com.google.inject.Inject;
import com.vexsoftware.votifier.VoteHandler;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierServerBootstrap;
import com.vexsoftware.votifier.net.server.VotifierSession;
import io.ibj.nuvotifier.legacyserver.protocol.v1crypto.RSAIO;
import io.ibj.nuvotifier.legacyserver.protocol.v1crypto.RSAKeygen;
import io.ibj.nuvotifier.platform.platform.LoggingAdapter;
import io.ibj.nuvotifier.platform.platform.scheduler.VotifierScheduler;
import com.vexsoftware.votifier.sponge.cmd.NVReloadCmd;
import com.vexsoftware.votifier.sponge.cmd.TestVoteCmd;
import com.vexsoftware.votifier.sponge.config.ConfigLoader;
import com.vexsoftware.votifier.sponge.event.VotifierEvent;
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 org.slf4j.Logger;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.args.GenericArguments;
import org.spongepowered.api.command.spec.CommandSpec;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameStartedServerEvent;
import org.spongepowered.api.event.game.state.GameStoppingServerEvent;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.text.Text;
import java.io.File;
import java.security.Key;
import java.security.KeyPair;
import java.util.HashMap;
import java.util.Map;
@Plugin(id = "nuvotifier", name = "NuVotifier", version = "3.0.0-SNAPSHOT", authors = "Ichbinjoe",
description = "Safe, smart, and secure Votifier server plugin")
public class VotifierPlugin implements VoteHandler, io.ibj.nuvotifier.platform.platform.VotifierPlugin, ForwardedVoteListener {
@Inject
public Logger logger;
private SLF4JLogger loggerAdapter;
@Inject
@ConfigDir(sharedRoot = false)
public File configDir;
private VotifierScheduler scheduler;
private boolean loadAndBind() {
// Load configuration.
ConfigLoader.loadConfig(this);
/*
* Create RSA directory and keys if it does not exist; otherwise, read
* keys.
*/
File rsaDirectory = new File(configDir, "rsa");
try {
if (!rsaDirectory.exists()) {
rsaDirectory.mkdir();
keyPair = RSAKeygen.generate(2048);
RSAIO.save(rsaDirectory, keyPair);
} else {
keyPair = RSAIO.load(rsaDirectory);
}
} catch (Exception ex) {
logger.error("Error creating or reading RSA tokens", ex);
return false;
}
debug = ConfigLoader.getSpongeConfig().debug;
// Load Votifier tokens.
ConfigLoader.getSpongeConfig().tokens.forEach((s, s2) -> {
tokens.put(s, KeyCreator.createKeyFrom(s2));
logger.info("Loaded token for website: " + s);
});
// Initialize the receiver.
final String host = ConfigLoader.getSpongeConfig().host;
final int port = ConfigLoader.getSpongeConfig().port;
if (!debug)
logger.info("QUIET mode enabled!");
if (port >= 0) {
final boolean disablev1 = ConfigLoader.getSpongeConfig().disableV1Protocol;
if (disablev1) {
logger.info("------------------------------------------------------------------------------");
logger.info("Votifier protocol v1 parsing has been disabled. Most voting websites do not");
logger.info("currently support the modern Votifier protocol in NuVotifier.");
logger.info("------------------------------------------------------------------------------");
}
this.bootstrap = new VotifierServerBootstrap(host, port, this, disablev1);
this.bootstrap.start(err -> {
});
} else {
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("Your Votifier port is less than 0, so we assume you do NOT want to start the");
getLogger().info("votifier port server! Votifier will not listen for votes over any port, and");
getLogger().info("will only listen for pluginMessaging forwarded votes!");
getLogger().info("------------------------------------------------------------------------------");
}
if (ConfigLoader.getSpongeConfig().forwarding != null) {
String method = ConfigLoader.getSpongeConfig().forwarding.method.toLowerCase(); //Default to lower case for case-insensitive searches
if ("none".equals(method)) {
getLogger().info("Method none selected for vote forwarding: Votes will not be received from a forwarder.");
} else if ("pluginmessaging".equals(method)) {
String channel = ConfigLoader.getSpongeConfig().forwarding.pluginMessaging.channel;
try {
forwardingMethod = new SpongePluginMessagingForwardingSink(this, channel, this);
getLogger().info("Receiving votes over PluginMessaging channel '" + channel + "'.");
} catch (RuntimeException e) {
logger.error("NuVotifier could not set up PluginMessaging for vote forwarding!", e);
}
} else {
logger.error("No vote forwarding method '" + method + "' known. Defaulting to noop implementation.");
}
}
return true;
}
private void halt() {
// Shut down the network handlers.
if (bootstrap != null) {
bootstrap.shutdown();
bootstrap = null;
}
if (forwardingMethod != null) {
forwardingMethod.halt();
forwardingMethod = null;
}
}
public boolean reload() {
try {
halt();
} catch (Exception ex) {
logger.error("On halt, an exception was thrown. This may be fine!", ex);
}
if (loadAndBind()) {
logger.info("Reload was successful.");
return true;
} else {
try {
halt();
logger.error("On reload, there was a problem with the configuration. Votifier currently does nothing!");
} catch (Exception ex) {
logger.error("On reload, there was a problem loading, and we could not re-halt the server. Votifier is in an unstable state!", ex);
}
return false;
}
}
@Listener
public void onServerStart(GameStartedServerEvent event) {
this.scheduler = new SpongeScheduler(this);
this.loggerAdapter = new SLF4JLogger(logger);
CommandSpec nvreloadSpec = CommandSpec.builder()
.description(Text.of("Reloads NuVotifier"))
.permission("nuvotifier.reload")
.executor(new NVReloadCmd(this)).build();
Sponge.getCommandManager().register(this, nvreloadSpec, "nvreload");
CommandSpec testvoteSpec = CommandSpec.builder()
.arguments(GenericArguments.allOf(GenericArguments.string(Text.of("args"))))
.description(Text.of("Sends a test vote to the server's listeners"))
.permission("nuvotifier.testvote")
.executor(new TestVoteCmd(this)).build();
Sponge.getCommandManager().register(this, testvoteSpec, "testvote");
if (!loadAndBind()) {
gracefulExit();
}
}
@Listener
public void onServerStop(GameStoppingServerEvent event) {
halt();
logger.info("Votifier disabled.");
}
public Logger getLogger() {
return logger;
}
/**
* The current Votifier version.
*/
private String version;
/**
* The server bootstrap.
*/
private VotifierServerBootstrap bootstrap;
/**
* The RSA key pair.
*/
private KeyPair keyPair;
/**
* Debug mode flag
*/
private boolean debug;
/**
* Keys used for websites.
*/
private Map<String, Key> tokens = new HashMap<>();
private ForwardingVoteSink forwardingMethod;
private void gracefulExit() {
logger.error("Votifier did not initialize properly!");
}
@Override
public LoggingAdapter getPluginLogger() {
return loggerAdapter;
}
@Override
public VotifierScheduler getScheduler() {
return scheduler;
}
public boolean isDebug() {
return debug;
}
@Override
public Map<String, Key> getTokens() {
return tokens;
}
@Override
public KeyPair getProtocolV1Key() {
return keyPair;
}
public File getConfigDir() {
return configDir;
}
@Override
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
logger.info("Got a protocol v1 vote record from " + remoteAddress + " -> " + vote);
} else {
logger.info("Got a protocol v2 vote record from " + remoteAddress + " -> " + vote);
}
}
Sponge.getScheduler().createTaskBuilder()
.execute(() -> {
VotifierEvent event = new VotifierEvent(vote, Sponge.getCauseStackManager().getCurrentCause());
Sponge.getEventManager().post(event);
})
.submit(this);
}
@Override
public void onError(Throwable throwable, boolean alreadyHandledVote, String remoteAddress) {
if (debug) {
if (alreadyHandledVote) {
logger.error("Vote processed, however an exception " +
"occurred with a vote from " + remoteAddress, throwable);
} else {
logger.error("Unable to process vote from " + remoteAddress, throwable);
}
} else if (!alreadyHandledVote) {
logger.error("Unable to process vote from " + remoteAddress);
}
}
@Override
public void onForward(final Vote v) {
if (debug) {
logger.info("Got a forwarded vote -> " + v);
}
Sponge.getScheduler().createTaskBuilder()
.execute(() -> {
VotifierEvent event = new VotifierEvent(v, Sponge.getCauseStackManager().getCurrentCause());
Sponge.getEventManager().post(event);
})
.submit(this);
}
}
@@ -1,28 +0,0 @@
package com.vexsoftware.votifier.sponge.cmd;
import com.vexsoftware.votifier.sponge.VotifierPlugin;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.spec.CommandExecutor;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.format.TextColors;
public class NVReloadCmd implements CommandExecutor {
private final VotifierPlugin plugin;
public NVReloadCmd(VotifierPlugin plugin) {
this.plugin = plugin;
}
@Override
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException {
src.sendMessage(Text.builder("Reloading NuVotifier...").color(TextColors.GRAY).build());
if (plugin.reload())
return CommandResult.success();
else
return CommandResult.empty();
}
}
@@ -1,41 +0,0 @@
package com.vexsoftware.votifier.sponge.cmd;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.server.VotifierSession;
import com.vexsoftware.votifier.sponge.VotifierPlugin;
import com.vexsoftware.votifier.util.ArgsToVote;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.spec.CommandExecutor;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.format.TextColors;
import java.util.Collection;
public class TestVoteCmd implements CommandExecutor {
private final VotifierPlugin plugin;
public TestVoteCmd(VotifierPlugin plugin) {
this.plugin = plugin;
}
@Override
public CommandResult execute(CommandSource sender, CommandContext args) throws CommandException {
Vote v;
try {
// well, this is a super nice feature of sponge. unfortunately, no one else has these features. Therefore, GET RID OF IT
Collection<String> a = args.getAll("args");
v = ArgsToVote.parse((String[]) a.toArray());
} catch (IllegalArgumentException e) {
sender.sendMessage(Text.builder("Error while parsing arguments to create test vote: " + e.getMessage()).color(TextColors.DARK_RED).build());
sender.sendMessage(Text.builder("Usage hint: /testvote [username] [serviceName=?] [username=?] [address=?] [localTimestamp=?] [timestamp=?]").color(TextColors.GRAY).build());
return CommandResult.empty();
}
plugin.onVoteReceived(v, VotifierSession.ProtocolVersion.TEST, "localhost.test");
return CommandResult.success();
}
}
@@ -1,39 +0,0 @@
package com.vexsoftware.votifier.sponge.config;
import com.google.common.reflect.TypeToken;
import com.vexsoftware.votifier.sponge.VotifierPlugin;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.ConfigurationOptions;
import ninja.leaping.configurate.loader.ConfigurationLoader;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import java.io.File;
public class ConfigLoader {
private static VotifierPlugin plugin;
private static SpongeConfig spongeConfig;
public static void loadConfig(VotifierPlugin pl) {
plugin = pl;
if (!plugin.getConfigDir().exists()) {
plugin.getConfigDir().mkdirs();
}
try {
File file = new File(plugin.getConfigDir(), "config.yml");
if (!file.exists()) {
file.createNewFile();
}
ConfigurationLoader loader = YAMLConfigurationLoader.builder().setFile(file).build();
ConfigurationNode config = loader.load(ConfigurationOptions.defaults().setShouldCopyDefaults(true));
spongeConfig = config.getValue(TypeToken.of(SpongeConfig.class), new SpongeConfig());
loader.save(config);
} catch (Exception e) {
plugin.getLogger().error("Could not load config.", e);
}
}
public static SpongeConfig getSpongeConfig() {
return spongeConfig;
}
}
@@ -1,55 +0,0 @@
package com.vexsoftware.votifier.sponge.config;
import com.vexsoftware.votifier.util.TokenUtil;
import ninja.leaping.configurate.objectmapping.Setting;
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
import org.spongepowered.api.Sponge;
import java.util.Collections;
import java.util.Map;
@ConfigSerializable
public class SpongeConfig {
@Setting(comment = "The IP to listen to. Use 0.0.0.0 if you wish to listen to all interfaces on your server. (All IP addresses)\n" +
"This defaults to the IP you have configured your server to listen on, or 0.0.0.0 if you have not configured this.")
public String host = Sponge.getServer().getBoundAddress().isPresent() ? Sponge.getServer().getBoundAddress().get().getAddress().getHostAddress() : "0.0.0.0";
@Setting(comment = "Port to listen for new votes on")
public int port = 8192;
@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 = 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" +
"using NuVotifier's proxy forwarding mechanism, enabling this option will increase your server's security.")
public boolean disableV1Protocol = false;
@Setting(comment = "All tokens, labeled by the serviceName of each server list.\n" +
"Default token for all server lists, if another isn't supplied.")
public Map<String, String> tokens = Collections.singletonMap("default", TokenUtil.newToken());
@Setting(comment = "Configuration section for all vote forwarding to NuVotifier")
public Forwarding forwarding = new Forwarding();
@ConfigSerializable
public static class Forwarding {
@Setting(comment = "Sets whether to set up a remote method for fowarding. Supported methods:\n" +
"- none - Does not set up a forwarding method.\n" +
"- pluginMessaging - Sets up plugin messaging")
public String method = "none";
@Setting
public PluginMessaging pluginMessaging = new PluginMessaging();
@ConfigSerializable
public static class PluginMessaging {
@Setting
public String channel = "nuvotifier:votes";
}
}
}
@@ -1,39 +0,0 @@
package com.vexsoftware.votifier.sponge.forwarding;
import com.vexsoftware.votifier.sponge.VotifierPlugin;
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;
import org.spongepowered.api.network.ChannelBuf;
import org.spongepowered.api.network.RawDataListener;
import org.spongepowered.api.network.RemoteConnection;
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.p = p;
}
private final VotifierPlugin p;
private final ChannelBinding.RawDataChannel channelBinding;
@Override
public void halt() {
channelBinding.removeListener(this);
}
@Override
public void handlePayload(ChannelBuf channelBuf, RemoteConnection remoteConnection, Platform.Type type) {
byte[] msgDirBuf = channelBuf.readBytes(channelBuf.available());
try {
this.handlePluginMessage(msgDirBuf);
} catch (Exception e) {
p.getLogger().error("There was an unknown error when processing a forwarded vote.", e);
}
}
}
@@ -1,25 +0,0 @@
# The IP to listen to. Use 0.0.0.0 if you wish to listen to all interfaces on your server. (All IP addresses)
# This defaults to the IP you have configured your server to listen on, or 0.0.0.0 if you have not configured this.
host: %ip%
# Port to listen for new votes on
port: 8192
# 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.
disable-v1-protocol: false
# All tokens, labeled by the serviceName of each server list.
tokens:
# Default token for all server lists, if another isn't supplied.
default: %default_token%
# Configuration section for all vote forwarding to NuVotifier
forwarding:
# Sets whether to set up a remote method for fowarding. Supported methods:
# - none - Does not set up a forwarding method.
# - pluginMessaging - Sets up plugin messaging
method: none
pluginMessaging:
channel: nuvotifier:votes
-70
View File
@@ -1,70 +0,0 @@
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()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
}
apply plugin: 'com.github.johnrengelman.shadow'
dependencies {
compile project(":bukkit")
compile project(":bungeecord")
compile project(":sponge")
compile project(":velocity")
}
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 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"
}
artifacts {
tasks.shadowJar.outputs.files.each {
archives(it) {
builtBy 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
+4
View File
@@ -0,0 +1,4 @@
dependencies {
api group: "com.velocitypowered", name: "velocity-api", version: "1.0-SNAPSHOT"
implementation project(":model")
}
@@ -1,11 +1,12 @@
package com.vexsoftware.votifier.velocity.event;
import com.vexsoftware.votifier.model.Vote;
import io.ibj.nuvotifier.model.NuVote;
public class VotifierEvent {
private final Vote vote;
private final NuVote vote;
public VotifierEvent(Vote vote) {
public VotifierEvent(NuVote vote) {
this.vote = vote;
}
-5
View File
@@ -1,5 +0,0 @@
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(":common")
}
@@ -1,66 +0,0 @@
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.ChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.vexsoftware.votifier.model.Vote;
import io.ibj.nuvotifier.platform.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;
public final class OnlineForwardPluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource {
private final String fallbackServer;
private final VotifierPlugin plugin;
private final ChannelIdentifier velocityChannelId;
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);
}
@Override
public void forward(Vote v) {
Optional<Player> p = plugin.getServer().getPlayer(v.getUsername());
Optional<ServerConnection> sc = p.flatMap(Player::getCurrentServer);
if (sc.isPresent() &&
serverFilter.isAllowed(sc.get().getServerInfo().getName())
) {
if (forwardSpecific(new VelocityBackendServer(plugin.getServer(), sc.get().getServer()), v)) {
return;
}
}
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());
else if (!forwardSpecific(new VelocityBackendServer(plugin.getServer(), fs.get()), v))
attemptToAddToCache(v, fallbackServer);
}
@Subscribe
public void onServerConnected(final ServerConnectedEvent e) { //Attempt to resend any votes that were previously cached.
BackendServer server = new VelocityBackendServer(plugin.getServer(), e.getServer());
onServerConnect(server);
handlePlayerSwitch(server, e.getPlayer().getUsername());
}
@Subscribe
public void onPluginMessage(PluginMessageEvent e) {
if (e.getIdentifier().equals(velocityChannelId)) {
e.setResult(PluginMessageEvent.ForwardResult.handled());
}
}
}
@@ -1,35 +0,0 @@
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.ChannelIdentifier;
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSource;
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
public class PluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource {
private final VotifierPlugin plugin;
private final ChannelIdentifier velocityChannelId;
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);
}
@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());
}
}
}
@@ -1,42 +0,0 @@
package com.vexsoftware.votifier.velocity;
import io.ibj.nuvotifier.platform.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);
}
}
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff. Show more