Compare commits

...
58 Commits
Author SHA1 Message Date
Joseph Hirschfeld 9944bb7077 Release 2.5.2 2019-04-27 00:20:31 -04:00
Joseph Hirschfeld 7d43b7399f Do not relocate google code from sponge
Fixes an issue where our shaded google code collided with Sponge,
causing mass sadness. Caused due to b745236
2019-04-27 00:17:04 -04:00
Joseph Hirschfeld 5d797065cb Update version to 2.5.2-SNAPSHOT 2019-04-25 23:58:10 -04:00
Joseph Hirschfeld 43c8489574 Release 2.5.1 2019-04-25 23:55:53 -04:00
Joseph Hirschfeld 348dbca900 Trim whitespace on either side of b64 encoded keys 2019-04-25 23:52:47 -04:00
Joseph Hirschfeld 395c63a80b update dev repo 2019-03-29 23:35:14 -04:00
Joseph Hirschfeld 99afd0ca71 rework env 2019-03-23 16:39:25 -04:00
Joseph Hirschfeld 2243be4954 missing env 2019-03-23 16:37:31 -04:00
Joseph Hirschfeld 8a73b0bf9c fix 2019-03-23 16:37:09 -04:00
Joseph Hirschfeld 595b7ed75f use ibj.io nexus 2019-03-23 16:16:43 -04:00
Joseph Hirschfeld 759b7a886b use openjdk 12 2019-03-23 16:14:14 -04:00
Joseph Hirschfeld eb68085746 fix some stuff in the jenkinsfile 2019-03-23 15:48:44 -04:00
Joseph Hirschfeld 16b093e4d6 psh this should be safe 2019-03-23 15:25:06 -04:00
Joseph Hirschfeld a2848e43d0 remove drone 2019-03-23 15:19:06 -04:00
Joseph Hirschfeld 1bfa28af48 Add jenkinsfile 2019-03-23 15:18:59 -04:00
Joseph Hirschfeld b745236131 Changes to packaging to fix issues with thermos 2019-03-11 16:50:15 -04:00
Joseph Hirschfeld 4330d41286 Fix FAQ formatting 2019-03-11 16:37:07 -04:00
Andrew Steinborn 75228868f8 Fix annotation processor-based implementations (Sponge and Velocity) (#122)
Fixes #121.
2019-02-05 18:51:07 -05:00
Joseph Hirschfeld 671c6e17a9 Do not try to use format args for displaying bind error 2019-01-24 13:44:52 -05:00
Joseph Hirschfeld c3030d56e8 Add FAQ 2019-01-22 17:16:14 -05:00
Joseph Hirschfeld 9fe4d6b9c3 change authorship 2019-01-22 16:06:32 -05:00
Joseph Hirschfeld 99dbaa703a Default to debug mode
People are incapable of turning on debug mode. This should help.
2019-01-22 15:54:31 -05:00
Joseph Hirschfeld 0fa42b44cc Add more debug from RSAIO decoding 2019-01-17 12:24:30 -05:00
Joseph Hirschfeld 7d75bce02c update netty to 4.1.32.Final 2019-01-04 13:59:53 -05:00
Joseph Hirschfeld 60923598b9 Bump to 2.5.1-SNAPSHOT 2019-01-03 14:40:06 -05:00
Joseph Hirschfeld 0c6dc99bcc Release 2.5.0 2019-01-03 14:37:51 -05:00
Joseph Hirschfeld f5776dd1cb Add missing file 2019-01-03 14:36:03 -05:00
Joseph Hirschfeld 7134c12826 Migrate forward sink to common, add tests
Fix an issue where decoding multiple votes in the compressed format
would result in an exception. Adds tests to ensure this doesn't happen
anymore.

Fixes #115
2019-01-03 14:26:54 -05:00
Joseph Hirschfeld 8cc93daf70 Create test dev environment 2018-12-21 01:02:53 -05:00
McModknower 98dfc345e9 Use 'new File(parent, name)' instead of 'new File(parent + "/"+name)' (#113)
* Change 'new File(getDataFolder() + "/...")' to use the 2 argument constructor of File instead.

* Change 'new File(folder + "/...")' to use the 2 argument constructor of File instead
2018-12-14 11:25:46 -05:00
Joseph Hirschfeld 590b7618f3 Push experimental support for bulk vote forwarding 2018-12-10 12:53:32 -05:00
Joseph Hirschfeld 4cd2ce55fc Add ServerFilter, behavior for when online forwarding is used 2018-12-10 12:41:39 -05:00
McModknower 9fe98a92a9 Do not use "/" as a path seperator (#110)
Instead use two argument invocation of new File(). Adds compatibility with Windows hosts.

Co-Authored-By: McModknower <teetoll@t-online.de>
2018-11-09 12:47:07 -05:00
Andrew Steinborn c72972c286 Fix issues in #102 (#103)
* Port to the latest Velocity API

* Actually register channels
2018-09-19 16:06:47 -04:00
Andrew Steinborn 92a122eaf8 Port to the latest Velocity API (#102) 2018-09-19 15:42:11 -04:00
Joseph Hirschfeld 67117b38e3 Add pluginMessage vote cache throttling. 2018-09-11 22:14:46 -04:00
Joseph Hirschfeld 9255062aff Fix configuration defaults, add memory cache ttl 2018-09-11 21:44:21 -04:00
Joseph Hirschfeld fe45e48447 We are 2.4.2 now. Also, fix plugin versions 2018-09-10 14:07:01 -04:00
Joseph Hirschfeld 0391e9e4bf Gotta be a snapshot... 2018-09-10 14:03:23 -04:00
Joseph Hirschfeld 59fa4857e3 Remove slf4j from non slf4j ports, use LoggingAdapter 2018-09-10 13:58:35 -04:00
Andrew Steinborn 4d663737c1 Fixed reference to capitialized channel name (#98) 2018-09-04 09:42:13 -04:00
Joseph Hirschfeld 8380ffce9a Release 2.4.1 2018-09-03 11:33:14 -04:00
Joseph Hirschfeld b9e648bba8 Spigot 1.13.1 wants these lowercase... 2018-09-03 11:31:42 -04:00
Joseph Hirschfeld c34551e630 Bump version to 2.4.1-SNAPSHOT 2018-09-02 22:21:19 -04:00
Joseph Hirschfeld d931cc2464 2.4.0 Release 2018-09-02 22:20:44 -04:00
Andrew Steinborn 07f81eb781 Velocity support (#94)
* Started porting to Velocity

* Heading down into the rabbit hole.

This begins a refactor of NuVotifier to more easily support other platforms
in the future. Much of this work is required to ensure feature-for-feature
compatibility between NuVotifier for BungeeCord and NuVotifier for Velocity.

Doesn't compile!

* Fixes the compile, adds SLF4J stuff.

Far from done!

* Independent Votifier server bootstrap

* Use independent Votifier server bootstrap across all platforms

* Laying the stones for Velocity forwarding

* Add vote forwarding to Velocity port

* It compiles, enables, and accepts a vote! (Still unfinished.)

* Vote forwarding works.

* Fix Sponge plugin message forwarding config

* Work around BungeeCord problems:

- JsonObject#keySet() is missing in Gson 2.8.0
- Bungee's "special" logging situation

* Remove Velocity repo

* Resolve issues that came up during review

* Handle a null error consumer

* Make the setup documentation proxy-agnostic

* Fix grammar.

* Add ParallelBlock repo
2018-09-02 19:00:57 -04:00
Joseph Hirschfeld c0ef61c819 Fix usage documentation regarding 1.13 channels 2018-09-02 14:11:44 -04:00
Joseph Hirschfeld 09bbfa8629 Add nvreload documentation 2018-08-23 00:37:49 -04:00
Joseph Hirschfeld aef038ef3a So gradle was getting mad at me, so i updated shadow. 2018-08-23 00:35:49 -04:00
Joseph Hirschfeld ac3827625d Add bukkit and bungee nvreload commands 2018-08-23 00:34:24 -04:00
Joseph Hirschfeld acc092f955 Fix a race condition in APMFS
Fixes the condition where there are never any failures passed back to be
re-added to the cache since failures is returned far before
dumpVotesToServer populates it.

This one hurt to find, but at least we got it!
2018-08-03 11:18:48 -04:00
Joseph Hirschfeld b494e1dc9d Fix eviction logging 2018-08-02 12:04:02 -04:00
Joseph Hirschfeld 27a368bd08 This is a snapshot! (Sorry build servers) 2018-08-02 12:01:48 -04:00
Joseph Hirschfeld 3c5b533a44 ignore gradletasknamecache
idk why its there, but it is, and i don't like it.
2018-08-02 12:00:37 -04:00
Joseph Hirschfeld f569c725d0 Signify that we are 1.13 compliant.
We should be at least. We just kick off events, no major
incompatibility.
2018-08-02 12:00:20 -04:00
Joseph Hirschfeld a09bc5a131 change default plugin messaging channel. Fixes #90
from NuVotifier to NuVotifier:votes. Gotta love stupid api changes that
have no reason to happen.
2018-08-02 12:00:01 -04:00
Joseph Hirschfeld c4e4f6e416 Close serverChannel and serverGroup in sync. Fixes #80
Patch originally authored by Time6628 on NytroCO/NuVotifier commit
5ee279bef03da9510952008e5ee5c8ada546cdb9 with modifications. In
addition, changes were applied to all three listeners.

Root issue caused by the fact that sometimes cleanups took longer than
Bukkit / anything else decided the jar holding the code should be open.
2018-08-02 11:47:17 -04:00
Parker Turchan 6ed181e835 Make the plugin actually work on Sponge API7 and cleanup a few other things (#85)
* fix injection problems

* more attempts at fixing weird NPEs

* fix injection issues and a bit of code cleanup

* fix some problems with the config and change it back to YAML so old configs still work, may add a converter to convert old YAML configs to HOCON in the future.

* clean up some other little things

* move constructor of ConfigLoader to loadConfig
2018-08-02 11:31:52 -04:00
123 changed files with 3765 additions and 1031 deletions

No files matched your search

-16
View File
@@ -1,16 +0,0 @@
pipeline:
build:
image: cogniteev/oracle-java
commands: ./gradlew build test --no-daemon
publish:
image: cogniteev/oracle-java
commands:
- ./gradlew publishversionedPublicationToParallelBlockRepository
- ./gradlew publishLatestToParallelBlockRaw
environment:
- PUBLISH=yes
- LATEST_BUILD=yes
secrets: [SNAPSHOT_REPO, RELEASE_REPO, REPO_USERNAME, REPO_PASSWORD, RAW_UPLOAD_PATH]
when:
branch: master
event: push
+1
View File
@@ -46,3 +46,4 @@ debug.txt
# gradle stuff
.gradle/
.gradletasknamecache
Vendored
+37
View File
@@ -0,0 +1,37 @@
pipeline {
agent {
docker { image 'openjdk:12-jdk' }
}
stages {
stage('Build') {
steps {
sh './gradlew build --no-daemon'
}
}
stage('Test') {
steps {
sh './gradlew test --no-daemon'
}
}
stage('Publish') {
when {
branch "master"
}
environment {
SNAPSHOT_REPO = credentials('ibj-nexus-snapshot-repo')
RELEASE_REPO = credentials('ibj-nexus-release-repo')
RAW_UPLOAD_PATH = credentials('ibj-nexus-raw-path')
REPO = credentials('ibj-nexus-access')
PUBLISH = 'true'
LATEST_BUILD = 'true'
}
steps {
sh './gradlew publishVersionedPublicationToIbjRepository'
sh './gradlew publishLatestToIbjBlockRaw'
}
}
}
}
+2 -8
View File
@@ -3,7 +3,7 @@ import org.apache.tools.ant.filters.ReplaceTokens
allprojects {
group 'com.vexsoftware'
version '2.3.7'
version '2.5.2'
}
subprojects {
@@ -14,13 +14,7 @@ subprojects {
repositories {
mavenCentral()
maven {
url 'https://hub.spigotmc.org/nexus/content/groups/public/'
}
maven {
url "https://oss.sonatype.org/content/groups/public/"
}
maven {
url "http://repo.spongepowered.org/maven"
url 'https://nexus.ibj.io/repository/maven-public/'
}
}
@@ -0,0 +1,57 @@
package com.vexsoftware.votifier;
import com.vexsoftware.votifier.platform.scheduler.ScheduledVotifierTask;
import com.vexsoftware.votifier.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();
}
}
}
@@ -21,35 +21,31 @@ package com.vexsoftware.votifier;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.vexsoftware.votifier.forwarding.BukkitPluginMessagingForwardingSink;
import com.vexsoftware.votifier.forwarding.ForwardedVoteListener;
import com.vexsoftware.votifier.forwarding.ForwardingVoteSink;
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
import com.vexsoftware.votifier.support.forwarding.ForwardingVoteSink;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.model.VotifierEvent;
import com.vexsoftware.votifier.net.VotifierServerBootstrap;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.net.protocol.VoteInboundHandler;
import com.vexsoftware.votifier.net.protocol.VotifierGreetingHandler;
import com.vexsoftware.votifier.net.protocol.VotifierProtocolDifferentiator;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAIO;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAKeygen;
import com.vexsoftware.votifier.platform.JavaUtilLogger;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
import com.vexsoftware.votifier.util.KeyCreator;
import com.vexsoftware.votifier.util.TokenUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
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.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyPair;
@@ -76,14 +72,9 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
private String version;
/**
* The server channel.
* The server bootstrap.
*/
private Channel serverChannel;
/**
* The event group handling the channel.
*/
private NioEventLoopGroup serverGroup;
private VotifierServerBootstrap bootstrap;
/**
* The RSA key pair.
@@ -101,22 +92,18 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
private Map<String, Key> tokens = new HashMap<>();
private ForwardingVoteSink forwardingMethod;
private VotifierScheduler scheduler;
private LoggingAdapter pluginLogger;
@Override
public void onEnable() {
NuVotifierBukkit.instance = this;
// Set the plugin version.
version = getDescription().getVersion();
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");
YamlConfiguration cfg;
File rsaDirectory = new File(getDataFolder() + "/rsa");
File config = new File(getDataFolder(), "config.yml");
/*
* Use IP address from server.properties as a default for
@@ -143,7 +130,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
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.write(cfgStr, config, StandardCharsets.UTF_8);
Files.asCharSink(config, StandardCharsets.UTF_8).write(cfgStr);
/*
* Remind hosted server admins to be sure they have the right
@@ -161,11 +148,13 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
getLogger().info("------------------------------------------------------------------------------");
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Error creating configuration file", ex);
gracefulExit();
return;
return false;
}
}
YamlConfiguration cfg;
File rsaDirectory = new File(getDataFolder(), "rsa");
// Load configuration.
cfg = YamlConfiguration.loadConfiguration(config);
@@ -184,13 +173,18 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
} catch (Exception ex) {
getLogger().log(Level.SEVERE,
"Error reading configuration file or RSA tokens", ex);
gracefulExit();
return;
return false;
}
debug = cfg.getBoolean("debug", false);
// the quiet flag always runs priority to the debug flag
if (cfg.isBoolean("quiet")) {
debug = !cfg.getBoolean("quiet");
} else {
// otherwise, default to being noisy
debug = cfg.getBoolean("debug", true);
}
// Load Votifier tokens.
// Load Votifier tokens.
ConfigurationSection tokenSection = cfg.getConfigurationSection("tokens");
if (tokenSection != null) {
@@ -209,8 +203,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
} catch (IOException e) {
getLogger().log(Level.SEVERE,
"Error generating Votifier token", e);
gracefulExit();
return;
return false;
}
getLogger().info("------------------------------------------------------------------------------");
getLogger().info("No tokens were found in your configuration, so we've generated one for you.");
@@ -223,8 +216,9 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
// Initialize the receiver.
final String host = cfg.getString("host", hostAddr);
final int port = cfg.getInt("port", 8192);
if (debug)
getLogger().info("DEBUG mode enabled!");
if (!debug)
getLogger().info("QUIET mode enabled!");
if (port >= 0) {
final boolean disablev1 = cfg.getBoolean("disable-v1-protocol");
if (disablev1) {
@@ -234,37 +228,8 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
getLogger().info("------------------------------------------------------------------------------");
}
serverGroup = new NioEventLoopGroup(1);
new ServerBootstrap()
.channel(NioServerSocketChannel.class)
.group(serverGroup)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) throws Exception {
channel.attr(VotifierSession.KEY).set(new VotifierSession());
channel.attr(VotifierPlugin.KEY).set(NuVotifierBukkit.this);
channel.pipeline().addLast("greetingHandler", new VotifierGreetingHandler());
channel.pipeline().addLast("protocolDifferentiator", new VotifierProtocolDifferentiator(false, !disablev1));
channel.pipeline().addLast("voteHandler", new VoteInboundHandler(NuVotifierBukkit.this));
}
})
.bind(host, port)
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
serverChannel = future.channel();
getLogger().info("Votifier enabled on socket " + serverChannel.localAddress() + ".");
} else {
SocketAddress socketAddress = future.channel().localAddress();
if (socketAddress == null) {
socketAddress = new InetSocketAddress(host, port);
}
getLogger().log(Level.SEVERE, "Votifier was not able to bind to " + socketAddress, future.cause());
}
}
});
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");
@@ -290,22 +255,70 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
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();
if (!loadAndBind()) {
gracefulExit();
setEnabled(false); // safer to just bomb out
}
}
@Override
public void onDisable() {
// Shut down the network handlers.
if (serverGroup != null) {
if (serverChannel != null)
serverChannel.close();
serverGroup.shutdownGracefully();
}
if (forwardingMethod != null) {
forwardingMethod.halt();
}
halt();
getLogger().info("Votifier disabled.");
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender instanceof ConsoleCommandSender) {
reload();
} else {
sender.sendMessage(ChatColor.RED + "For security and stability, only console may run this command!");
}
return true;
}
private void 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.");
} 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);
}
}
}
private void gracefulExit() {
getLogger().log(Level.SEVERE, "Votifier did not initialize properly!");
}
@@ -319,13 +332,14 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
return instance;
}
/**
* Gets the version.
*
* @return The version
*/
public String getVersion() {
return version;
@Override
public LoggingAdapter getPluginLogger() {
return pluginLogger;
}
@Override
public VotifierScheduler getScheduler() {
return scheduler;
}
public boolean isDebug() {
@@ -343,7 +357,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
}
@Override
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
getLogger().info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
@@ -351,12 +365,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
getLogger().info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
}
}
Bukkit.getScheduler().runTask(this, new Runnable() {
@Override
public void run() {
Bukkit.getPluginManager().callEvent(new VotifierEvent(vote));
}
});
Bukkit.getScheduler().runTask(this, () -> Bukkit.getPluginManager().callEvent(new VotifierEvent(vote)));
}
@Override
@@ -378,11 +387,6 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
if (debug) {
getLogger().info("Got a forwarded vote -> " + v);
}
Bukkit.getScheduler().runTask(this, new Runnable() {
@Override
public void run() {
Bukkit.getPluginManager().callEvent(new VotifierEvent(v));
}
});
Bukkit.getScheduler().runTask(this, () -> Bukkit.getPluginManager().callEvent(new VotifierEvent(v)));
}
}
@@ -1,34 +1,31 @@
package com.vexsoftware.votifier.forwarding;
import com.google.gson.JsonObject;
import com.vexsoftware.votifier.NuVotifierBukkit;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.util.GsonInst;
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSink;
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.messaging.PluginMessageListener;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
/**
* Created by Joe Hirschfeld on 10/20/2015.
*/
public class BukkitPluginMessagingForwardingSink implements ForwardingVoteSink, PluginMessageListener {
public class BukkitPluginMessagingForwardingSink extends AbstractPluginMessagingForwardingSink implements PluginMessageListener {
public BukkitPluginMessagingForwardingSink(Plugin p, String channel, ForwardedVoteListener listener) {
super(listener);
Validate.notNull(channel, "Channel cannot be null.");
this.channel = channel;
Bukkit.getMessenger().registerIncomingPluginChannel(p, channel, this);
this.listener = listener;
this.p = p;
}
private final Plugin p;
private final String channel;
private final ForwardedVoteListener listener;
@Override
public void halt() {
@@ -38,10 +35,7 @@ public class BukkitPluginMessagingForwardingSink implements ForwardingVoteSink,
@Override
public void onPluginMessageReceived(String s, Player player, byte[] bytes) {
try {
String message = new String(bytes, StandardCharsets.UTF_8);
JsonObject jsonObject = GsonInst.gson.fromJson(message, JsonObject.class);
Vote v = new Vote(jsonObject);
listener.onForward(v);
this.handlePluginMessage(bytes);
} catch (Exception e) {
NuVotifierBukkit.getInstance().getLogger().log(Level.SEVERE, "There was an unknown error when processing a forwarded vote.", e);
}
@@ -1,13 +0,0 @@
package com.vexsoftware.votifier.forwarding;
/**
* Represents a method at which to receive forwarded votes.
*/
public interface ForwardingVoteSink {
/**
* Stop or close any oustanding network interfaces. Occurs on the onDisable method of a plugin.
*/
void halt();
}
+1 -5
View File
@@ -5,10 +5,6 @@ host: %ip%
# Port to listen for new votes on
port: 8192
# Whether or not to print debug messages. In a production system, this should be set to false.
# This is useful when initially setting up NuVotifier to ensure votes are being delivered.
debug: false
# Setting this option to true will disable handling of Protocol v1 packets. While the old protocol is not secure, this
# option is currently not recommended as most voting sites only support the old protocol at present. However, if you are
# using NuVotifier's proxy forwarding mechanism, enabling this option will increase your server's security.
@@ -26,4 +22,4 @@ forwarding:
# - pluginMessaging - Sets up plugin messaging
method: none
pluginMessaging:
channel: NuVotifier
channel: nuvotifier:votes
+9 -1
View File
@@ -2,4 +2,12 @@ name: Votifier
main: com.vexsoftware.votifier.NuVotifierBukkit
version: @app.version@
description: A plugin that gets notified when votes are made for the server on toplists.
authors: [ParallelBlock, blakeman8192, Kramer, tuxed]
authors: [Ichbinjoe, blakeman8192, Kramer, tuxed]
api-version: 1.13
commands:
nvreload:
description: Reloads the NuVotifier configuration
permission: nuvotifier.console
permission-message: "You do not have permission to run this command."
usage: "/nvreload"
@@ -0,0 +1,28 @@
package com.vexsoftware.votifier.bungee;
import com.google.common.base.Preconditions;
import com.vexsoftware.votifier.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();
}
}
@@ -0,0 +1,53 @@
package com.vexsoftware.votifier.bungee;
import com.vexsoftware.votifier.platform.scheduler.ScheduledVotifierTask;
import com.vexsoftware.votifier.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();
}
}
}
@@ -4,33 +4,32 @@ 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.VotifierPlugin;
import com.vexsoftware.votifier.net.VotifierServerBootstrap;
import com.vexsoftware.votifier.platform.BackendServer;
import com.vexsoftware.votifier.platform.JavaUtilLogger;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import com.vexsoftware.votifier.platform.ProxyVotifierPlugin;
import com.vexsoftware.votifier.bungee.events.VotifierEvent;
import com.vexsoftware.votifier.bungee.forwarding.ForwardingVoteSource;
import com.vexsoftware.votifier.bungee.forwarding.OnlineForwardPluginMessagingForwardingSource;
import com.vexsoftware.votifier.bungee.forwarding.PluginMessagingForwardingSource;
import com.vexsoftware.votifier.bungee.forwarding.cache.FileVoteCache;
import com.vexsoftware.votifier.bungee.forwarding.cache.MemoryVoteCache;
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.bungee.forwarding.proxy.ProxyForwardingVoteSource;
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
import com.vexsoftware.votifier.support.forwarding.ForwardingVoteSource;
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
import com.vexsoftware.votifier.support.forwarding.cache.FileVoteCache;
import com.vexsoftware.votifier.support.forwarding.cache.MemoryVoteCache;
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.support.forwarding.proxy.ProxyForwardingVoteSource;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.net.protocol.VoteInboundHandler;
import com.vexsoftware.votifier.net.protocol.VotifierGreetingHandler;
import com.vexsoftware.votifier.net.protocol.VotifierProtocolDifferentiator;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAIO;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAKeygen;
import com.vexsoftware.votifier.util.KeyCreator;
import com.vexsoftware.votifier.util.TokenUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
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.plugin.Command;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
@@ -40,31 +39,23 @@ import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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, VotifierPlugin {
public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlugin {
/**
* The server channel.
*/
private Channel serverChannel;
/**
* The event group handling the channel.
*/
private NioEventLoopGroup serverGroup;
private VotifierServerBootstrap bootstrap;
/**
* The RSA key pair.
@@ -86,15 +77,19 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
*/
private ForwardingVoteSource forwardingMethod;
@Override
public void onEnable() {
private VotifierScheduler scheduler;
private LoggingAdapter pluginLogger;
private void loadAndBind() {
scheduler = new BungeeScheduler(this);
pluginLogger = new JavaUtilLogger(getLogger());
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
// Handle configuration.
File config = new File(getDataFolder() + "/config.yml");
File rsaDirectory = new File(getDataFolder() + "/rsa");
File config = new File(getDataFolder() , "config.yml");
File rsaDirectory = new File(getDataFolder() , "rsa");
Configuration configuration;
if (!config.exists()) {
@@ -108,7 +103,7 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
String cfgStr = new String(ByteStreams.toByteArray(getResourceAsStream("bungeeConfig.yml")), StandardCharsets.UTF_8);
String token = TokenUtil.newToken();
cfgStr = cfgStr.replace("%default_token%", token);
Files.write(cfgStr, config, StandardCharsets.UTF_8);
Files.asCharSink(config, StandardCharsets.UTF_8).write(cfgStr);
/*
* Remind hosted server admins to be sure they have the right
@@ -184,9 +179,14 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
// Initialize the receiver.
final String host = configuration.getString("host", "0.0.0.0");
final int port = configuration.getInt("port", 8192);
debug = configuration.getBoolean("debug", false);
if (debug)
getLogger().info("DEBUG mode enabled!");
if (configuration.get("quiet") != null)
debug = !configuration.getBoolean("quiet");
else
debug = configuration.getBoolean("debug", true);
if (!debug)
getLogger().info("QUIET mode enabled!");
final boolean disablev1 = configuration.getBoolean("disable-v1-protocol");
if (disablev1) {
@@ -197,41 +197,9 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
}
// Must set up server asynchronously due to BungeeCord goofiness.
FutureTask<?> initTask = new FutureTask<>(Executors.callable(new Runnable() {
@Override
public void run() {
serverGroup = new NioEventLoopGroup(2);
new ServerBootstrap()
.channel(NioServerSocketChannel.class)
.group(serverGroup)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) throws Exception {
channel.attr(VotifierSession.KEY).set(new VotifierSession());
channel.attr(VotifierPlugin.KEY).set(NuVotifier.this);
channel.pipeline().addLast("greetingHandler", new VotifierGreetingHandler());
channel.pipeline().addLast("protocolDifferentiator", new VotifierProtocolDifferentiator(false, !disablev1));
channel.pipeline().addLast("voteHandler", new VoteInboundHandler(NuVotifier.this));
}
})
.bind(host, port)
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
serverChannel = future.channel();
getLogger().info("Votifier enabled on socket " + serverChannel.localAddress() + ".");
} else {
SocketAddress socketAddress = future.channel().localAddress();
if (socketAddress == null) {
socketAddress = new InetSocketAddress(host, port);
}
getLogger().log(Level.SEVERE, "Votifier was not able to bind to " + socketAddress, future.cause());
}
}
});
}
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 {
@@ -251,7 +219,9 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
if ("none".equals(cacheMethod)) {
getLogger().info("Vote cache none selected for caching: votes that cannot be immediately delivered will be lost.");
} else if ("memory".equals(cacheMethod)) {
voteCache = new MemoryVoteCache(ProxyServer.getInstance().getServers().size());
voteCache = new MemoryVoteCache(
ProxyServer.getInstance().getServers().size(), this,
fwdCfg.getInt("pluginMessaging.memory.cacheTime", -1));
getLogger().info("Using in-memory cache for votes that are not able to be delivered.");
} else if ("file".equals(cacheMethod)) {
try {
@@ -263,12 +233,17 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
getLogger().log(Level.SEVERE, "Unload to load file cache. Votes will be lost!", e);
}
}
int dumpRate = fwdCfg.getInt("pluginMessaging.dumpRate", 5);
ServerFilter filter = new ServerFilter(
fwdCfg.getStringList("pluginMessaging.excludedServers"),
fwdCfg.getBoolean("pluginMessaging.whitelist", false)
);
if (!fwdCfg.getBoolean("pluginMessaging.onlySendToJoinedServer")) {
List<String> ignoredServers = fwdCfg.getStringList("pluginMessaging.excludedServers");
try {
forwardingMethod = new PluginMessagingForwardingSource(channel, ignoredServers, this, voteCache);
forwardingMethod = new PluginMessagingForwardingSource(channel, filter, this, voteCache, dumpRate);
getLogger().info("Forwarding votes over PluginMessaging channel '" + channel + "' for vote forwarding!");
} catch (RuntimeException e) {
getLogger().log(Level.SEVERE, "NuVotifier could not set up PluginMessaging for vote forwarding!", e);
@@ -277,7 +252,7 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
try {
String fallbackServer = fwdCfg.getString("pluginMessaging.joinedServerFallback", null);
if (fallbackServer != null && fallbackServer.isEmpty()) fallbackServer = null;
forwardingMethod = new OnlineForwardPluginMessagingForwardingSource(channel, this, voteCache, fallbackServer);
forwardingMethod = new OnlineForwardPluginMessagingForwardingSource(channel, this, filter, voteCache, fallbackServer, dumpRate);
} catch (RuntimeException e) {
getLogger().log(Level.SEVERE, "NuVotifier could not set up PluginMessaging for vote forwarding!", e);
}
@@ -312,30 +287,70 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
}
}
forwardingMethod = new ProxyForwardingVoteSource(this, serverGroup, serverList, null);
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 onDisable() {
public void onEnable() {
loadAndBind();
getProxy().getPluginManager().registerCommand(this, new Command("nvreload") {
@Override
public void execute(CommandSender sender, String[] args) {
if (sender instanceof ProxiedPlayer)
sender.sendMessage(ChatColor.RED + "For security and stability, only console may run this command!");
else
reload();
}
});
}
private void halt() {
// Shut down the network handlers.
if (serverChannel != null)
serverChannel.close();
serverGroup.shutdownGracefully();
if (bootstrap != null) {
bootstrap.shutdown();
bootstrap = null;
}
if (forwardingMethod != null) {
forwardingMethod.halt();
forwardingMethod = null;
}
}
public void 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.");
} 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);
}
}
}
@Override
public void onDisable() {
halt();
getLogger().info("Votifier disabled.");
}
@Override
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
getLogger().info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
@@ -344,20 +359,10 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
}
}
getProxy().getScheduler().runAsync(this, new Runnable() {
@Override
public void run() {
getProxy().getPluginManager().callEvent(new VotifierEvent(vote));
}
});
getProxy().getScheduler().runAsync(this, () -> getProxy().getPluginManager().callEvent(new VotifierEvent(vote)));
if (forwardingMethod != null) {
getProxy().getScheduler().runAsync(this, new Runnable() {
@Override
public void run() {
forwardingMethod.forward(vote);
}
});
getProxy().getScheduler().runAsync(this, () -> forwardingMethod.forward(vote));
}
}
@@ -386,11 +391,29 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
}
@Override
public String getVersion() {
return getDescription().getVersion();
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);
}
}
@@ -0,0 +1,60 @@
package com.vexsoftware.votifier.bungee;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.platform.BackendServer;
import com.vexsoftware.votifier.support.forwarding.AbstractPluginMessagingForwardingSource;
import com.vexsoftware.votifier.support.forwarding.ServerFilter;
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
import 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,24 +1,23 @@
package com.vexsoftware.votifier.bungee.forwarding;
package com.vexsoftware.votifier.bungee;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
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;
import java.util.List;
public class PluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
public PluginMessagingForwardingSource(String channel, List<String> ignoredServers, NuVotifier nuVotifier, VoteCache cache) {
super(channel, ignoredServers, nuVotifier, cache);
public PluginMessagingForwardingSource(String channel, ServerFilter serverFilter, NuVotifier nuVotifier, VoteCache cache, int dumpRate) {
super(channel, serverFilter, nuVotifier, cache, dumpRate);
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
}
protected PluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache) {
super(channel, nuVotifier, voteCache);
protected PluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache, int dumpRate) {
super(channel, nuVotifier, voteCache, dumpRate);
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
}
@@ -29,6 +28,6 @@ public class PluginMessagingForwardingSource extends AbstractPluginMessagingForw
@EventHandler
public void onServerConnected(final ServerConnectedEvent e) { //Attempt to resend any votes that were previously cached.
handleServerConnected(e);
onServerConnect(new BungeeBackendServer(e.getServer().getInfo()));
}
}
@@ -1,141 +0,0 @@
package com.vexsoftware.votifier.bungee.forwarding;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.bungee.forwarding.cache.FileVoteCache;
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.model.Vote;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.event.EventHandler;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public abstract class AbstractPluginMessagingForwardingSource implements ForwardingVoteSource {
public AbstractPluginMessagingForwardingSource(String channel, List<String> ignoredServers, NuVotifier nuVotifier, VoteCache cache) {
ProxyServer.getInstance().registerChannel(channel);
this.channel = channel;
this.nuVotifier = nuVotifier;
this.cache = cache;
this.ignoredServers = ignoredServers;
}
protected AbstractPluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache) {
this(channel, null, nuVotifier, voteCache);
}
protected final NuVotifier nuVotifier;
protected final String channel;
protected final VoteCache cache;
protected final List<String> ignoredServers;
@Override
public void forward(Vote v) {
byte[] rawData = v.serialize().toString().getBytes(StandardCharsets.UTF_8);
for (ServerInfo s : ProxyServer.getInstance().getServers().values()) {
if (ignoredServers != null && ignoredServers.contains(s.getName())) continue;
if (!forwardSpecific(s, rawData)) attemptToAddToCache(v, s.getName());
}
}
protected boolean forwardSpecific(ServerInfo connection, Vote vote) {
byte[] rawData = vote.serialize().toString().getBytes(StandardCharsets.UTF_8);
return connection.sendData(channel, rawData, false);
}
private boolean forwardSpecific(ServerInfo connection, byte[] data) {
return connection.sendData(channel, data, false);
}
@Override
public void halt() {
ProxyServer.getInstance().unregisterChannel(channel);
if (cache != null && cache instanceof FileVoteCache) {
try {
((FileVoteCache) cache).halt();
} catch (IOException e) {
nuVotifier.getLogger().log(Level.SEVERE, "Unable to save cached votes, votes will be lost.", e);
}
}
}
protected void handleServerConnected(final ServerConnectedEvent e) {
if (cache == null) return;
final String serverName = e.getServer().getInfo().getName();
final Collection<Vote> cachedVotes = cache.evict(serverName);
final Collection<Vote> failedVotes = dumpVotesToServer(cachedVotes, e.getServer().getInfo(), "server '" + serverName + "'");
for (Vote v : failedVotes)
cache.addToCache(v, serverName);
}
protected void attemptToAddToCache(Vote v, String server) {
if (cache != null) {
cache.addToCache(v, server);
if (nuVotifier.isDebug())
nuVotifier.getLogger().info("Added to forwarding cache: " + v + " -> " + server);
} else if (nuVotifier.isDebug())
nuVotifier.getLogger().severe("Could not immediately send vote to backend, vote lost! " + v + " -> " + server);
}
protected void attempToAddToPlayerCache(Vote v, String player) {
if (cache != null) {
cache.addToCachePlayer(v, player);
if (nuVotifier.isDebug())
nuVotifier.getLogger().info("Added to forwarding cache: " + v + " -> (player) " + player);
} else if (nuVotifier.isDebug())
nuVotifier.getLogger().severe("Could not immediately send vote to backend, vote lost! " + v + " -> (player) " + player);
}
// returns a collection of failed votes
private Collection<Vote> dumpVotesToServer(Collection<Vote> cachedVotes, ServerInfo target, String identifier) {
List<Vote> failures = new ArrayList<>();
if (!cachedVotes.isEmpty()) {
nuVotifier.getProxy().getScheduler().schedule(nuVotifier, new Runnable() {
@Override
public void run() {
int evicted = 0;
int unsuccessfulEvictions = 0;
for (Vote v : cachedVotes) {
if (forwardSpecific(target, v)) {
evicted++;
} else {
// Re-add to cache to send later.
failures.add(v);
unsuccessfulEvictions++;
}
}
if (nuVotifier.isDebug()) {
nuVotifier.getLogger().info("Successfully evicted " + evicted + " votes to " + identifier + ".");
if (unsuccessfulEvictions > 0) {
nuVotifier.getLogger().info("Held " + unsuccessfulEvictions + " votes for " + identifier + ".");
}
}
}
}, 1, TimeUnit.SECONDS);
}
return failures;
}
protected void handlePlayerSwitch(ServerConnectedEvent e) {
if (cache == null) return;
if (ignoredServers != null && ignoredServers.contains(e.getServer().getInfo().getName())) return;
String playerName = e.getPlayer().getName();
final Collection<Vote> cachedVotes = cache.evictPlayer(playerName);
final Collection<Vote> failedVotes = dumpVotesToServer(cachedVotes, e.getServer().getInfo(), "player '" + playerName);
for (Vote v : failedVotes)
cache.addToCachePlayer(v, playerName);
}
}
@@ -1,58 +0,0 @@
package com.vexsoftware.votifier.bungee.forwarding;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.model.Vote;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
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, VoteCache cache, String fallbackServer) {
super(channel, nuVotifier, cache);
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 || !forwardSpecific(p.getServer().getInfo(), v)) {
ServerInfo serverInfo = ProxyServer.getInstance().getServers().get(fallbackServer);
// nowhere to fall back to, yet still not online. lets save this vote yet!
if (serverInfo == null)
attempToAddToPlayerCache(v, v.getUsername());
else if (!forwardSpecific(serverInfo, v))
attemptToAddToCache(v, fallbackServer);
}
}
// The below is to allow us to share code since the event handlers from the parent class don't get
// picked up.
@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.
handleServerConnected(e);
}
@EventHandler
public void onPlayerSwitch(ServerConnectedEvent e) {
handlePlayerSwitch(e);
}
}
@@ -1,90 +0,0 @@
package com.vexsoftware.votifier.bungee.forwarding.cache;
import com.google.common.collect.ImmutableList;
import com.vexsoftware.votifier.model.Vote;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by Joe Hirschfeld on 10/20/2015.
*/
public class MemoryVoteCache implements VoteCache {
public MemoryVoteCache(int initialSize) {
voteCache = new HashMap<>(initialSize);
this.playerVoteCache = new HashMap<>();
}
protected final Map<String, Collection<Vote>> voteCache;
protected final Map<String, Collection<Vote>> playerVoteCache;
protected final ReentrantLock cacheLock = new ReentrantLock();
@Override
public Collection<String> getCachedServers() {
cacheLock.lock();
try {
return ImmutableList.copyOf(voteCache.keySet());
} finally {
cacheLock.unlock();
}
}
@Override
public void addToCache(Vote v, String server) {
if (server == null) throw new NullPointerException();
cacheLock.lock();
try {
Collection<Vote> voteCollection = voteCache.get(server);
if (voteCollection == null) {
voteCollection = new ArrayList<>();
voteCache.put(server, voteCollection);
}
voteCollection.add(v);
} finally {
cacheLock.unlock();
}
}
@Override
public void addToCachePlayer(Vote v, String player) {
if (player == null) throw new NullPointerException();
cacheLock.lock();
try {
Collection<Vote> voteCollection = playerVoteCache.get(player);
if (voteCollection == null) {
voteCollection = new ArrayList<>();
playerVoteCache.put(player, voteCollection);
}
voteCollection.add(v);
} finally {
cacheLock.unlock();
}
}
@Override
public Collection<Vote> evictPlayer(String player) {
if (player == null) throw new NullPointerException();
cacheLock.lock();
try {
Collection<Vote> fromCollection = playerVoteCache.remove(player);
return fromCollection != null ? fromCollection : ImmutableList.of();
} finally {
cacheLock.unlock();
}
}
@Override
public Collection<Vote> evict(String server) {
if (server == null) throw new NullPointerException();
cacheLock.lock();
try {
Collection<Vote> fromCollection = voteCache.remove(server);
return fromCollection != null ? fromCollection : ImmutableList.of();
} finally {
cacheLock.unlock();
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
name: NuVotifier
main: com.vexsoftware.votifier.bungee.NuVotifier
version: @app.version@
author: ParallelBlock, blakeman8192, Kramer, tuxed
author: Ichbinjoe, blakeman8192, Kramer, tuxed
+12 -9
View File
@@ -5,10 +5,6 @@ host: 0.0.0.0
# Port to listen for new votes on
port: 8192
# Whether or not to print debug messages. In a production system, this should be set to false.
# This is useful when initially setting up NuVotifier to ensure votes are being delivered.
debug: false
# All tokens, labeled by the serviceName of each server list.
tokens:
# Default token for all server lists, if another isn't supplied.
@@ -26,21 +22,22 @@ forwarding:
# - proxy - Proxies votes to other NuVotifier servers from this server.
method: none
pluginMessaging:
channel: NuVotifier
channel: nuvotifier:votes
#Only send votes to certain servers. If a server is present on excludedServers, then votes will NOT be forwarded
#to them. If excludedServers is not present, votes will be sent to all servers.
#If onlySendToJoinedServer is true, this option is ignored.
# If onlySendToJoinedServer is true and a player is on one of these servers, NuVotifier will process the vote as if the
# player was not on the network at all.
#excludedServers:
#- server1
#- server2
# If uncommented, turns excludedServers into an includedServers, where only servers on the list will be used.
#whitelist: true
# Sets which cache to use if there are no players on the server that the vote is being sent to. Supported caches:
# - none - Votes will simply be lost when no players are present on the server.
# - memory - Votes will be cached in memory when no players are present. When Bungee is restarted, the votes will be lost.
# This method is NOT recommended for production systems!
# - file - Votes will be saved to a local file every 3 minutes and on shutdown and reloaded when Bungee is restarted. This is suitable for
# most set ups.
cache: file
@@ -50,11 +47,17 @@ forwarding:
#If you do not want the vote forwarded to a fallback, set this value to empty ('')
#ONLY USED IF onlySendToJoinedServer is true!!
joinedServerFallback: 'Hub'
# Defines how quickly to dump votes over a player's connection when offloading a cache in votes per second
dumpRate: 5
# Options for file caching.
file:
name: cached-votes.json
# days before a vote is considered 'dead' - removed from cache with a console warning
voteTTL: 10
cacheTime: 10
# Options for memory caching.
memory:
# days before a vote is considered 'dead' and removed from memory. All votes are removed when the server restarts. -1 signifies no TTL
cacheTime: -1
# Specify servers to proxy votes for.
proxy:
Hub:
+2 -2
View File
@@ -1,6 +1,6 @@
dependencies {
compile group: "io.netty", name: "netty-handler", version: "4.1.16.Final"
compile 'com.google.code.gson:gson:2.8.2'
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: "junit", name: "junit", version: "4.12"
testCompile group: "org.json", name: "json", version: "20180130" // retain this for testing reasons
@@ -1,17 +0,0 @@
package com.vexsoftware.votifier;
import io.netty.util.AttributeKey;
import java.security.Key;
import java.security.KeyPair;
import java.util.Map;
public interface VotifierPlugin {
AttributeKey<VotifierPlugin> KEY = AttributeKey.valueOf("votifier_plugin");
Map<String, Key> getTokens();
KeyPair getProtocolV1Key();
String getVersion();
}
@@ -0,0 +1,90 @@
package com.vexsoftware.votifier.net;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.vexsoftware.votifier.net.protocol.VoteInboundHandler;
import com.vexsoftware.votifier.net.protocol.VotifierGreetingHandler;
import com.vexsoftware.votifier.net.protocol.VotifierProtocolDifferentiator;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Objects;
import java.util.function.Consumer;
public class VotifierServerBootstrap {
private final String host;
private final int port;
private final NioEventLoopGroup bossLoopGroup;
private final NioEventLoopGroup eventLoopGroup;
private final VotifierPlugin plugin;
private final boolean v1Disable;
private Channel serverChannel;
public VotifierServerBootstrap(String host, int port, VotifierPlugin plugin, boolean v1Disable) {
this.host = host;
this.port = port;
this.plugin = plugin;
this.v1Disable = v1Disable;
this.bossLoopGroup = new NioEventLoopGroup(1, new ThreadFactoryBuilder().setDaemon(true).setNameFormat("Votifier NIO boss").build());
this.eventLoopGroup = new NioEventLoopGroup(1, new ThreadFactoryBuilder().setDaemon(true).setNameFormat("Votifier NIO worker").build());
}
public void start(Consumer<Throwable> error) {
Objects.requireNonNull(error, "error");
new ServerBootstrap()
.channel(NioServerSocketChannel.class)
.group(bossLoopGroup, eventLoopGroup)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) {
channel.attr(VotifierSession.KEY).set(new VotifierSession());
channel.attr(VotifierPlugin.KEY).set(plugin);
channel.pipeline().addLast("greetingHandler", new VotifierGreetingHandler());
channel.pipeline().addLast("protocolDifferentiator", new VotifierProtocolDifferentiator(false, !v1Disable));
channel.pipeline().addLast("voteHandler", new VoteInboundHandler(plugin));
}
})
.bind(host, port)
.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
serverChannel = future.channel();
plugin.getPluginLogger().info("Votifier enabled on socket " + serverChannel.localAddress() + ".");
error.accept(null);
} else {
SocketAddress socketAddress = future.channel().localAddress();
if (socketAddress == null) {
socketAddress = new InetSocketAddress(host, port);
}
plugin.getPluginLogger().error("Votifier was not able to bind to " + socketAddress.toString(), future.cause());
error.accept(future.cause());
}
});
}
public Bootstrap client() {
return new Bootstrap()
.channel(NioSocketChannel.class)
.group(eventLoopGroup);
}
public void shutdown() {
if (serverChannel != null) {
try {
serverChannel.close().syncUninterruptibly();
} catch (Exception e) {
plugin.getPluginLogger().error("Unable to shutdown server channel", e);
}
}
eventLoopGroup.shutdownGracefully();
bossLoopGroup.shutdownGracefully();
}
}
@@ -33,13 +33,12 @@ public class VoteInboundHandler extends SimpleChannelInboundHandler<Vote> {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
VotifierSession session = ctx.channel().attr(VotifierSession.KEY).get();
handler.onError(ctx.channel(), session.hasCompletedVote(), cause);
if (session.getVersion() == VotifierSession.ProtocolVersion.TWO) {
JsonObject object = new JsonObject();
object.addProperty("status", "error");
object.addProperty("cause", cause.getClass().getSimpleName());
@@ -1,6 +1,5 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.VotifierPlugin;
import com.vexsoftware.votifier.net.VotifierSession;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
@@ -14,11 +13,9 @@ import java.nio.charset.StandardCharsets;
*/
public class VotifierGreetingHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
public void channelActive(ChannelHandlerContext ctx) {
VotifierSession session = ctx.channel().attr(VotifierSession.KEY).get();
VotifierPlugin plugin = ctx.channel().attr(VotifierPlugin.KEY).get();
String version = "VOTIFIER " + plugin.getVersion() + " " + session.getChallenge() + "\n";
String version = "VOTIFIER 2 " + session.getChallenge() + "\n";
ByteBuf versionBuf = Unpooled.copiedBuffer(version, StandardCharsets.UTF_8);
ctx.writeAndFlush(versionBuf);
}
@@ -1,6 +1,6 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.VotifierPlugin;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSA;
import io.netty.buffer.ByteBuf;
@@ -16,7 +16,7 @@ import java.util.List;
*/
public class VotifierProtocol1Decoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) {
if (buf.readableBytes() < 256) {
return;
}
@@ -1,7 +1,7 @@
package com.vexsoftware.votifier.net.protocol;
import com.google.gson.JsonObject;
import com.vexsoftware.votifier.VotifierPlugin;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.util.GsonInst;
@@ -26,7 +26,7 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) {
// Determine the number of bytes that are available.
int readable = buf.readableBytes();
@@ -18,6 +18,7 @@
package com.vexsoftware.votifier.net.protocol.v1crypto;
import java.io.IOException;
import java.util.Base64;
import java.io.File;
import java.io.FileOutputStream;
@@ -59,6 +60,19 @@ public class RSAIO {
}
}
public static byte[] readB64File(File directory, String name) throws IOException {
File f = new File(directory, name);
byte[] contents = Files.readAllBytes(f.toPath());
String strContents = new String(contents);
strContents = strContents.trim();
try {
return Base64.getDecoder().decode(strContents);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Base64 decoding exception: This is probably due to a corrupted file, " +
"but in case it isn't, here is a b64 representation of what we read: " + new String(Base64.getEncoder().encode(contents), StandardCharsets.UTF_8), e);
}
}
/**
* Loads an RSA key pair from a directory. The directory must have the files
* "public.key" and "private.key".
@@ -69,14 +83,10 @@ public class RSAIO {
*/
public static KeyPair load(File directory) throws Exception {
// Read the public key file.
File publicKeyFile = new File(directory + "/public.key");
byte[] encodedPublicKey = Files.readAllBytes(publicKeyFile.toPath());
encodedPublicKey = Base64.getDecoder().decode(new String(encodedPublicKey, StandardCharsets.UTF_8));
byte[] encodedPublicKey = readB64File(directory, "public.key");
// Read the private key file.
File privateKeyFile = new File(directory + "/private.key");
byte[] encodedPrivateKey = Files.readAllBytes(privateKeyFile.toPath());
encodedPrivateKey = Base64.getDecoder().decode(new String(encodedPrivateKey, StandardCharsets.UTF_8));
byte[] encodedPrivateKey = readB64File(directory, "private.key");
// Instantiate and return the key pair.
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
@@ -0,0 +1,7 @@
package com.vexsoftware.votifier.platform;
public interface BackendServer {
String getName();
boolean sendPluginMessage(String channel, byte[] data);
}
@@ -0,0 +1,45 @@
package com.vexsoftware.votifier.platform;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JavaUtilLogger implements LoggingAdapter {
private final Logger logger;
public JavaUtilLogger(Logger logger) {
this.logger = logger;
}
@Override
public void error(String s) {
logger.severe(s);
}
@Override
public void error(String s, Object... o) {
logger.log(Level.SEVERE, s, o);
}
@Override
public void warn(String s) {
logger.warning(s);
}
@Override
public void warn(String s, Object... o) {
logger.log(Level.WARNING, s, o);
}
@Override
public void info(String s) {
logger.info(s);
}
@Override
public void info(String s, Object... o) {
logger.log(Level.INFO, s, o);
}
}
@@ -0,0 +1,13 @@
package com.vexsoftware.votifier.platform;
public interface LoggingAdapter {
void error(String s);
void error(String s, Object... o);
void warn(String s);
void warn(String s, Object... o);
void info(String s);
void info(String s, Object... o);
}
@@ -0,0 +1,10 @@
package com.vexsoftware.votifier.platform;
import java.util.Collection;
import java.util.Optional;
public interface ProxyVotifierPlugin extends VotifierPlugin {
Collection<BackendServer> getAllBackendServers();
Optional<BackendServer> getServer(String name);
}
@@ -0,0 +1,25 @@
package com.vexsoftware.votifier.platform;
import com.vexsoftware.votifier.VoteHandler;
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
import io.netty.util.AttributeKey;
import java.security.Key;
import java.security.KeyPair;
import java.util.Map;
public interface VotifierPlugin extends VoteHandler {
AttributeKey<VotifierPlugin> KEY = AttributeKey.valueOf("votifier_plugin");
Map<String, Key> getTokens();
KeyPair getProtocolV1Key();
LoggingAdapter getPluginLogger();
VotifierScheduler getScheduler();
default boolean isDebug() {
return false;
}
}
@@ -0,0 +1,53 @@
package com.vexsoftware.votifier.platform.scheduler;
import com.google.common.base.Preconditions;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceVotifierScheduler implements VotifierScheduler {
private final ScheduledExecutorService service;
public ScheduledExecutorServiceVotifierScheduler(ScheduledExecutorService service) {
this.service = Preconditions.checkNotNull(service, "service");
}
@Override
public ScheduledVotifierTask sync(Runnable runnable) {
return new ScheduledVotifierTaskWrapper(service.submit(runnable));
}
@Override
public ScheduledVotifierTask onPool(Runnable runnable) {
return new ScheduledVotifierTaskWrapper(service.submit(runnable));
}
@Override
public ScheduledVotifierTask delayedSync(Runnable runnable, int delay, TimeUnit unit) {
return new ScheduledVotifierTaskWrapper(service.schedule(runnable, delay, unit));
}
@Override
public ScheduledVotifierTask delayedOnPool(Runnable runnable, int delay, TimeUnit unit) {
return new ScheduledVotifierTaskWrapper(service.schedule(runnable, delay, unit));
}
@Override
public ScheduledVotifierTask repeatOnPool(Runnable runnable, int delay, int repeat, TimeUnit unit) {
return new ScheduledVotifierTaskWrapper(service.scheduleAtFixedRate(runnable, delay, repeat, unit));
}
private static class ScheduledVotifierTaskWrapper implements ScheduledVotifierTask {
private final Future<?> future;
private ScheduledVotifierTaskWrapper(Future<?> future) {
this.future = future;
}
@Override
public void cancel() {
future.cancel(false);
}
}
}
@@ -0,0 +1,5 @@
package com.vexsoftware.votifier.platform.scheduler;
public interface ScheduledVotifierTask {
void cancel();
}
@@ -0,0 +1,15 @@
package com.vexsoftware.votifier.platform.scheduler;
import java.util.concurrent.TimeUnit;
public interface VotifierScheduler {
ScheduledVotifierTask sync(Runnable runnable);
ScheduledVotifierTask onPool(Runnable runnable);
ScheduledVotifierTask delayedSync(Runnable runnable, int delay, TimeUnit unit);
ScheduledVotifierTask delayedOnPool(Runnable runnable, int delay, TimeUnit unit);
ScheduledVotifierTask repeatOnPool(Runnable runnable, int delay, int repeat, TimeUnit unit);
}
@@ -0,0 +1,46 @@
package com.vexsoftware.votifier.support.forwarding;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.vexsoftware.votifier.model.Vote;
import java.io.CharArrayReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public abstract class AbstractPluginMessagingForwardingSink implements ForwardingVoteSink {
public AbstractPluginMessagingForwardingSink(ForwardedVoteListener listener) {
this.listener = listener;
}
private final ForwardedVoteListener listener;
public void handlePluginMessage(byte[] message) {
String strMessage = new String(message, StandardCharsets.UTF_8);
try (CharArrayReader reader = new CharArrayReader(strMessage.toCharArray())) {
JsonReader r = new JsonReader(reader);
r.setLenient(true);
while (r.peek() != JsonToken.END_DOCUMENT) {
r.beginObject();
JsonObject o = new JsonObject();
while (r.hasNext()) {
String name = r.nextName();
if (r.peek() == JsonToken.NUMBER)
o.add(name, new JsonPrimitive(r.nextLong()));
else
o.add(name, new JsonPrimitive(r.nextString()));
}
r.endObject();
Vote v = new Vote(o);
listener.onForward(v);
}
} catch (IOException e) {
e.printStackTrace(); // Should never happen.
}
}
}
@@ -0,0 +1,156 @@
package com.vexsoftware.votifier.support.forwarding;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.platform.BackendServer;
import com.vexsoftware.votifier.platform.ProxyVotifierPlugin;
import com.vexsoftware.votifier.support.forwarding.cache.FileVoteCache;
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public abstract class AbstractPluginMessagingForwardingSource implements ForwardingVoteSource {
public AbstractPluginMessagingForwardingSource(String channel, ServerFilter serverFilter, ProxyVotifierPlugin plugin, VoteCache cache, int dumpRate) {
this.channel = channel;
this.plugin = plugin;
this.cache = cache;
this.serverFilter = serverFilter;
this.dumpRate = dumpRate;
}
protected AbstractPluginMessagingForwardingSource(String channel, ProxyVotifierPlugin plugin, VoteCache voteCache, int dumpRate) {
this(channel, null, plugin, voteCache, dumpRate);
}
protected final ProxyVotifierPlugin plugin;
protected final String channel;
protected final VoteCache cache;
protected final ServerFilter serverFilter;
private final int dumpRate;
@Override
public void forward(Vote v) {
byte[] rawData = v.serialize().toString().getBytes(StandardCharsets.UTF_8);
for (BackendServer server : plugin.getAllBackendServers()) {
if (!serverFilter.isAllowed(server.getName())) continue;
if (!forwardSpecific(server, rawData)) attemptToAddToCache(v, server.getName());
}
}
protected boolean forwardSpecific(BackendServer connection, Vote vote) {
byte[] rawData = vote.serialize().toString().getBytes(StandardCharsets.UTF_8);
return forwardSpecific(connection, rawData);
}
protected boolean forwardSpecific(BackendServer connection, Collection<Vote> votes) {
StringBuilder data = new StringBuilder();
for (Vote v : votes) {
data.append(v.serialize().toString());
}
return forwardSpecific(connection, data.toString().getBytes(StandardCharsets.UTF_8));
}
private boolean forwardSpecific(BackendServer connection, byte[] data) {
return connection.sendPluginMessage(channel, data);
}
@Override
public void halt() {
if (cache instanceof FileVoteCache) {
try {
((FileVoteCache) cache).halt();
} catch (IOException e) {
plugin.getPluginLogger().error("Unable to save cached votes, votes will be lost.", e);
}
}
}
protected void onServerConnect(BackendServer server) {
if (cache == null) return;
final Collection<Vote> cachedVotes = cache.evict(server.getName());
dumpVotesToServer(cachedVotes, server, "server '" + server + "'", failedVotes -> {
for (Vote v : failedVotes)
cache.addToCache(v, server.getName());
});
}
protected void attemptToAddToCache(Vote v, String server) {
if (cache != null) {
cache.addToCache(v, server);
if (plugin.isDebug())
plugin.getPluginLogger().info("Added to forwarding cache: " + v + " -> " + server);
} else if (plugin.isDebug())
plugin.getPluginLogger().error("Could not immediately send vote to backend, vote lost! " + v + " -> " + server);
}
protected void attemptToAddToPlayerCache(Vote v, String player) {
if (cache != null) {
cache.addToCachePlayer(v, player);
if (plugin.isDebug())
plugin.getPluginLogger().info("Added to forwarding cache: " + v + " -> (player) " + player);
} else if (plugin.isDebug())
plugin.getPluginLogger().error("Could not immediately send vote to backend, vote lost! " + v + " -> (player) " + player);
}
// returns a collection of failed votes
private void dumpVotesToServer(Collection<Vote> cachedVotes, BackendServer target, String identifier, Consumer<Collection<Vote>> cb) {
dumpVotesToServer(cachedVotes, target, identifier, 0, cb);
}
private void dumpVotesToServer(Collection<Vote> cachedVotes, BackendServer target, String identifier, int evictedAlready, Consumer<Collection<Vote>> cb) {
if (!cachedVotes.isEmpty()) {
plugin.getScheduler().delayedOnPool(() -> {
int evicted = 0;
Iterator<Vote> vi = cachedVotes.iterator();
Collection<Vote> chunk = new ArrayList<>(dumpRate);
while (vi.hasNext() && evicted < dumpRate) {
chunk.add(vi.next());
vi.remove();
}
if (forwardSpecific(target, chunk)) {
evicted += chunk.size();
if (evicted >= dumpRate && !cachedVotes.isEmpty()) {
// if we evicted everything we could but still need to evict more
dumpVotesToServer(cachedVotes, target, identifier, evictedAlready + evicted, cb);
return;
}
} else {
// so since our forwarding failed, break like we are done
cachedVotes.addAll(chunk);
}
if (plugin.isDebug()) {
plugin.getPluginLogger().info("Successfully evicted " + (evictedAlready + evicted) + " votes to " + identifier + ".");
if (!cachedVotes.isEmpty()) {
plugin.getPluginLogger().info("Held " + cachedVotes.size() + " votes for " + identifier + ".");
}
}
// cachedVotes contains any votes which have yet to be evicted
cb.accept(cachedVotes);
}, 1, TimeUnit.SECONDS);
} else {
cb.accept(cachedVotes);
}
}
protected void handlePlayerSwitch(BackendServer server, String playerName) {
if (cache == null) return;
if (!serverFilter.isAllowed(server.getName())) return;
final Collection<Vote> cachedVotes = cache.evictPlayer(playerName);
dumpVotesToServer(cachedVotes, server, "player '" + playerName + "'", failedVotes -> {
for (Vote v : failedVotes)
cache.addToCachePlayer(v, playerName);
});
}
}
@@ -1,4 +1,4 @@
package com.vexsoftware.votifier.forwarding;
package com.vexsoftware.votifier.support.forwarding;
import com.vexsoftware.votifier.model.Vote;
@@ -1,4 +1,4 @@
package com.vexsoftware.votifier.sponge.forwarding;
package com.vexsoftware.votifier.support.forwarding;
/**
* Represents a method at which to receive forwarded votes.
@@ -1,4 +1,4 @@
package com.vexsoftware.votifier.bungee.forwarding;
package com.vexsoftware.votifier.support.forwarding;
import com.vexsoftware.votifier.model.Vote;
@@ -0,0 +1,23 @@
package com.vexsoftware.votifier.support.forwarding;
import java.util.Collection;
import java.util.Collections;
public class ServerFilter {
public ServerFilter(Collection<String> names, boolean whitelist) {
this.names = names;
this.whitelist = whitelist;
}
public ServerFilter() {
this(Collections.EMPTY_SET, true);
}
private final Collection<String> names;
private final boolean whitelist;
public boolean isAllowed(String name) {
return names.contains(name) == whitelist;
}
}
@@ -1,48 +1,50 @@
package com.vexsoftware.votifier.bungee.forwarding.cache;
package com.vexsoftware.votifier.support.forwarding.cache;
import com.google.common.io.Files;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.platform.scheduler.ScheduledVotifierTask;
import com.vexsoftware.votifier.util.GsonInst;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FileVoteCache extends MemoryVoteCache {
private final Logger l;
private final LoggingAdapter l;
private final File cacheFile;
private final int voteTTL;
private final ScheduledTask saveTask;
private final ScheduledVotifierTask saveTask;
public FileVoteCache(int initialMemorySize, final Plugin plugin, File cacheFile, int voteTTL) throws IOException {
super(initialMemorySize);
public FileVoteCache(int initialMemorySize, final VotifierPlugin plugin, File cacheFile, long voteTTL) throws IOException {
super(initialMemorySize, plugin, voteTTL);
this.cacheFile = cacheFile;
this.voteTTL = voteTTL;
this.l = plugin.getLogger();
this.l = plugin.getPluginLogger();
load();
saveTask = ProxyServer.getInstance().getScheduler().schedule(plugin, new Runnable() {
@Override
public void run() {
try {
save();
} catch (IOException e) {
l.log(Level.SEVERE, "Unable to save cached votes, votes will be lost if you restart.", e);
}
saveTask = plugin.getScheduler().repeatOnPool(() -> {
try {
save();
} catch (IOException e) {
l.error("Unable to save cached votes, votes will be lost if you restart.", e);
}
}, 3, 3, TimeUnit.MINUTES);
}
private static Set<String> keySet(JsonObject object) {
Set<String> set = new HashSet<>();
for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
set.add(entry.getKey());
}
return set;
}
private void load() throws IOException {
// Load the cache from disk
JsonObject object;
@@ -70,12 +72,12 @@ public class FileVoteCache extends MemoryVoteCache {
JsonObject players = object.getAsJsonObject("players");
JsonObject servers = object.getAsJsonObject("servers");
for (String player : players.keySet()) {
playerVoteCache.put(player, readVotes(players.getAsJsonArray(player)));
for (String player : keySet(players)) {
playerVoteCache.putAll(player, readVotes(players.getAsJsonArray(player)));
}
for (String server : servers.keySet()) {
voteCache.put(server, readVotes(servers.getAsJsonArray(server)));
for (String server : keySet(servers)) {
voteCache.putAll(server, readVotes(servers.getAsJsonArray(server)));
}
if (resave) {
@@ -87,11 +89,11 @@ public class FileVoteCache extends MemoryVoteCache {
}
if (!cacheFile.renameTo(replacementFile)) {
l.log(Level.SEVERE, "Backup movement failed! Will not save.");
l.error("Backup movement failed! Will not save.");
return;
}
l.log(Level.WARNING, "Saving new vote cache format to file - backup moved to " + replacementFile.getAbsolutePath());
l.warn("Saving new vote cache format to file - backup moved to " + replacementFile.getAbsolutePath());
save();
}
}
@@ -102,7 +104,7 @@ public class FileVoteCache extends MemoryVoteCache {
JsonObject voteObject = voteArray.get(i).getAsJsonObject();
Vote v = new Vote(voteObject);
if (hasTimedOut(v))
l.log(Level.WARNING, "Purging out of date vote.", v);
l.warn("Purging out of date vote.", v);
else
votes.add(v);
}
@@ -114,8 +116,8 @@ public class FileVoteCache extends MemoryVoteCache {
JsonObject votesObject = new JsonObject();
votesObject.addProperty("version", 2);
try {
votesObject.add("players", serializeMap(playerVoteCache));
votesObject.add("servers", serializeMap(voteCache));
votesObject.add("players", serializeMap(playerVoteCache.asMap()));
votesObject.add("servers", serializeMap(voteCache.asMap()));
} finally {
cacheLock.unlock();
}
@@ -138,7 +140,7 @@ public class FileVoteCache extends MemoryVoteCache {
// if the vote is no longer valid, notify and remove
if (hasTimedOut(vote)) {
l.log(Level.WARNING, "Purging out of date vote.", vote);
l.warn("Purging out of date vote.", vote);
voteItr.remove();
} else {
array.add(vote.serialize());
@@ -154,11 +156,6 @@ public class FileVoteCache extends MemoryVoteCache {
return o;
}
private boolean hasTimedOut(Vote v) {
if (voteTTL == -1) return false;
// scale voteTTL to milliseconds
return v.getLocalTimestamp() + voteTTL * 24 * 60 * 60 * 1000 < System.currentTimeMillis();
}
public void halt() throws IOException {
saveTask.cancel();
@@ -0,0 +1,115 @@
package com.vexsoftware.votifier.support.forwarding.cache;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.platform.scheduler.ScheduledVotifierTask;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by Joe Hirschfeld on 10/20/2015.
*/
public class MemoryVoteCache implements VoteCache {
private final LoggingAdapter l;
private final long voteTTL;
protected final Multimap<String, Vote> voteCache;
protected final Multimap<String, Vote> playerVoteCache;
protected final ReentrantLock cacheLock = new ReentrantLock();
private final ScheduledVotifierTask sweepTask;
public MemoryVoteCache(int initialSize, VotifierPlugin p, long voteTTL) {
voteCache = HashMultimap.create(initialSize, 10); // 10 is a complete guess. I have no idea .-.
playerVoteCache = HashMultimap.create();
this.voteTTL = voteTTL;
this.l = p.getPluginLogger();
this.sweepTask = p.getScheduler().repeatOnPool(this::sweep, 12, 12, TimeUnit.HOURS);
}
@Override
public Collection<String> getCachedServers() {
cacheLock.lock();
try {
return ImmutableList.copyOf(voteCache.keySet());
} finally {
cacheLock.unlock();
}
}
@Override
public void addToCache(Vote v, String server) {
if (server == null) throw new NullPointerException();
cacheLock.lock();
try {
voteCache.put(server, v);
} finally {
cacheLock.unlock();
}
}
@Override
public void addToCachePlayer(Vote v, String player) {
if (player == null) throw new NullPointerException();
cacheLock.lock();
try {
playerVoteCache.put(player, v);
} finally {
cacheLock.unlock();
}
}
@Override
public Collection<Vote> evictPlayer(String player) {
if (player == null) throw new NullPointerException();
cacheLock.lock();
try {
return new HashSet<>(playerVoteCache.removeAll(player));
} finally {
cacheLock.unlock();
}
}
@Override
public Collection<Vote> evict(String server) {
if (server == null) throw new NullPointerException();
cacheLock.lock();
try {
return new HashSet<>(voteCache.removeAll(server));
} finally {
cacheLock.unlock();
}
}
public void sweep() {
sweep(voteCache);
sweep(playerVoteCache);
}
private void sweep(Multimap<?, Vote> m) {
Iterator<Vote> vi = m.values().iterator();
while (vi.hasNext()) {
Vote v = vi.next();
if (hasTimedOut(v)) {
l.warn("Purging out of date vote.", v);
vi.remove();
}
}
}
protected boolean hasTimedOut(Vote v) {
if (voteTTL == -1) return false;
// scale voteTTL to milliseconds
return v.getLocalTimestamp() + voteTTL * 24 * 60 * 60 * 1000 < System.currentTimeMillis();
}
}
@@ -1,4 +1,4 @@
package com.vexsoftware.votifier.bungee.forwarding.cache;
package com.vexsoftware.votifier.support.forwarding.cache;
import com.vexsoftware.votifier.model.Vote;
@@ -1,17 +1,15 @@
package com.vexsoftware.votifier.bungee.forwarding.proxy;
package com.vexsoftware.votifier.support.forwarding.proxy;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.bungee.forwarding.ForwardingVoteSource;
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.bungee.forwarding.proxy.client.VotifierProtocol2Encoder;
import com.vexsoftware.votifier.bungee.forwarding.proxy.client.VotifierProtocol2HandshakeHandler;
import com.vexsoftware.votifier.bungee.forwarding.proxy.client.VotifierResponseHandler;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.support.forwarding.ForwardingVoteSource;
import com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.support.forwarding.proxy.client.VotifierProtocol2Encoder;
import com.vexsoftware.votifier.support.forwarding.proxy.client.VotifierProtocol2HandshakeHandler;
import com.vexsoftware.votifier.support.forwarding.proxy.client.VotifierResponseHandler;
import com.vexsoftware.votifier.model.Vote;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
@@ -23,20 +21,20 @@ import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.function.Supplier;
public class ProxyForwardingVoteSource implements ForwardingVoteSource {
private static final int MAX_RETRIES = 5;
private final NuVotifier plugin;
private final NioEventLoopGroup eventLoopGroup;
private final VotifierPlugin plugin;
private final Supplier<Bootstrap> nettyBootstrap;
private final List<BackendServer> backendServers;
private final VoteCache voteCache;
private static final StringDecoder STRING_DECODER = new StringDecoder(StandardCharsets.US_ASCII);
public ProxyForwardingVoteSource(NuVotifier plugin, NioEventLoopGroup eventLoopGroup, List<BackendServer> backendServers, VoteCache voteCache) {
public ProxyForwardingVoteSource(VotifierPlugin plugin, Supplier<Bootstrap> nettyBootstrap, List<BackendServer> backendServers, VoteCache voteCache) {
this.plugin = plugin;
this.eventLoopGroup = eventLoopGroup;
this.nettyBootstrap = nettyBootstrap;
this.backendServers = backendServers;
this.voteCache = voteCache;
}
@@ -54,21 +52,19 @@ public class ProxyForwardingVoteSource implements ForwardingVoteSource {
}
private void forwardVote(final BackendServer server, final Vote v, final int tries) {
new Bootstrap()
.channel(NioSocketChannel.class)
.group(eventLoopGroup)
nettyBootstrap.get()
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) throws Exception {
protected void initChannel(NioSocketChannel channel) {
channel.pipeline().addLast(new DelimiterBasedFrameDecoder(256, true, Delimiters.lineDelimiter()));
channel.pipeline().addLast(new ReadTimeoutHandler(8, TimeUnit.SECONDS));
channel.pipeline().addLast(new ReadTimeoutHandler(5, TimeUnit.SECONDS));
channel.pipeline().addLast(STRING_DECODER);
channel.pipeline().addLast(new VotifierProtocol2Encoder(server.key));
channel.pipeline().addLast(new VotifierProtocol2HandshakeHandler(v, new VotifierResponseHandler() {
@Override
public void onSuccess() {
if (plugin.isDebug()) {
plugin.getLogger().info("Successfully forwarded vote " + v + " to " + server.address + ".");
plugin.getPluginLogger().info("Successfully forwarded vote " + v + " to " + server.address + ".");
}
}
@@ -80,12 +76,9 @@ public class ProxyForwardingVoteSource implements ForwardingVoteSource {
}
})
.connect(server.address)
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
handleFailure(server, v, future.cause(), tries);
}
.addListener((ChannelFutureListener) future -> {
if (!future.isSuccess()) {
handleFailure(server, v, future.cause(), tries);
}
});
}
@@ -106,18 +99,13 @@ public class ProxyForwardingVoteSource implements ForwardingVoteSource {
}
if (plugin.isDebug()) {
plugin.getLogger().log(Level.SEVERE, msg, cause);
plugin.getPluginLogger().error(msg, cause);
} else {
plugin.getLogger().log(Level.SEVERE, msg);
plugin.getPluginLogger().error(msg);
}
if (willRetry) {
plugin.getProxy().getScheduler().schedule(plugin, new Runnable() {
@Override
public void run() {
forwardVote(server, v, tries + 1);
}
}, nextDelay, TimeUnit.SECONDS);
plugin.getScheduler().delayedOnPool(() -> forwardVote(server, v, tries + 1), nextDelay, TimeUnit.SECONDS);
}
}
@@ -1,4 +1,4 @@
package com.vexsoftware.votifier.bungee.forwarding.proxy.client;
package com.vexsoftware.votifier.support.forwarding.proxy.client;
import com.vexsoftware.votifier.model.Vote;
@@ -1,4 +1,4 @@
package com.vexsoftware.votifier.bungee.forwarding.proxy.client;
package com.vexsoftware.votifier.support.forwarding.proxy.client;
import com.google.gson.JsonObject;
import io.netty.buffer.ByteBuf;
@@ -1,6 +1,6 @@
package com.vexsoftware.votifier.bungee.forwarding.proxy.client;
package com.vexsoftware.votifier.support.forwarding.proxy.client;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@@ -9,16 +9,16 @@ import io.netty.handler.codec.CorruptedFrameException;
public class VotifierProtocol2HandshakeHandler extends SimpleChannelInboundHandler<String> {
private final Vote toSend;
private final VotifierResponseHandler responseHandler;
private final NuVotifier nuVotifier;
private final VotifierPlugin nuVotifier;
public VotifierProtocol2HandshakeHandler(Vote toSend, VotifierResponseHandler responseHandler, NuVotifier nuVotifier) {
public VotifierProtocol2HandshakeHandler(Vote toSend, VotifierResponseHandler responseHandler, VotifierPlugin nuVotifier) {
this.toSend = toSend;
this.responseHandler = responseHandler;
this.nuVotifier = nuVotifier;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
protected void channelRead0(ChannelHandlerContext ctx, String s) {
String[] handshakeContents = s.split(" ");
if (handshakeContents.length != 3) {
throw new CorruptedFrameException("Handshake is not valid.");
@@ -26,7 +26,7 @@ public class VotifierProtocol2HandshakeHandler extends SimpleChannelInboundHandl
VoteRequest request = new VoteRequest(handshakeContents[2], toSend);
if (nuVotifier.isDebug()) {
nuVotifier.getLogger().info("Sent request: " + request.toString());
nuVotifier.getPluginLogger().info("Sent request: " + request.toString());
}
ctx.writeAndFlush(request);
ctx.pipeline().addLast(new VotifierProtocol2ResponseHandler(responseHandler));
@@ -35,7 +35,7 @@ public class VotifierProtocol2HandshakeHandler extends SimpleChannelInboundHandl
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
responseHandler.onFailure(cause);
ctx.close();
}
@@ -1,4 +1,4 @@
package com.vexsoftware.votifier.bungee.forwarding.proxy.client;
package com.vexsoftware.votifier.support.forwarding.proxy.client;
import com.google.gson.JsonObject;
import com.vexsoftware.votifier.util.GsonInst;
@@ -13,7 +13,7 @@ public class VotifierProtocol2ResponseHandler extends SimpleChannelInboundHandle
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
JsonObject object = GsonInst.gson.fromJson(msg, JsonObject.class);
String status = object.get("status").getAsString();
if (status.equals("ok")) {
@@ -26,7 +26,7 @@ public class VotifierProtocol2ResponseHandler extends SimpleChannelInboundHandle
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
responseHandler.onFailure(cause);
ctx.close();
}
@@ -1,4 +1,4 @@
package com.vexsoftware.votifier.bungee.forwarding.proxy.client;
package com.vexsoftware.votifier.support.forwarding.proxy.client;
public interface VotifierResponseHandler {
void onSuccess();
@@ -1,19 +1,14 @@
package com.vexsoftware.votifier.util.standalone;
import com.vexsoftware.votifier.VoteHandler;
import com.vexsoftware.votifier.VotifierPlugin;
import com.vexsoftware.votifier.net.VotifierServerBootstrap;
import com.vexsoftware.votifier.platform.JavaUtilLogger;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.net.protocol.VoteInboundHandler;
import com.vexsoftware.votifier.net.protocol.VotifierGreetingHandler;
import com.vexsoftware.votifier.net.protocol.VotifierProtocolDifferentiator;
import io.netty.bootstrap.ServerBootstrap;
import com.vexsoftware.votifier.platform.scheduler.ScheduledExecutorServiceVotifierScheduler;
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
import java.security.Key;
@@ -21,45 +16,36 @@ import java.security.KeyPair;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.logging.Logger;
public class StandaloneVotifierPlugin implements VotifierPlugin, VoteHandler {
public class StandaloneVotifierPlugin implements VotifierPlugin {
private final Map<String, Key> tokens;
private final VoteReceiver receiver;
private final KeyPair v1Key;
private final InetSocketAddress bind;
private NioEventLoopGroup group;
private final VotifierScheduler scheduler;
private VotifierServerBootstrap bootstrap;
public StandaloneVotifierPlugin(Map<String, Key> tokens, VoteReceiver receiver, KeyPair v1Key, InetSocketAddress bind) {
this.receiver = receiver;
this.bind = bind;
this.tokens = Collections.unmodifiableMap(new HashMap<>(tokens));
this.v1Key = v1Key;
this.scheduler = new ScheduledExecutorServiceVotifierScheduler(Executors.newScheduledThreadPool(1));
}
public Throwable start() throws Throwable {
group = new NioEventLoopGroup(2);
public void start() {
start(o -> {});
}
ChannelFuture future = new ServerBootstrap()
.channel(NioServerSocketChannel.class)
.group(group)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) throws Exception {
channel.attr(VotifierSession.KEY).set(new VotifierSession());
channel.attr(VotifierPlugin.KEY).set(StandaloneVotifierPlugin.this);
channel.pipeline().addLast("greetingHandler", new VotifierGreetingHandler());
channel.pipeline().addLast("protocolDifferentiator", new VotifierProtocolDifferentiator(false, v1Key != null));
channel.pipeline().addLast("voteHandler", new VoteInboundHandler(StandaloneVotifierPlugin.this));
}
})
.bind(bind)
.syncUninterruptibly();
if (!future.isSuccess()) {
return future.cause();
} else {
return null;
public void start(Consumer<Throwable> error) {
if (bootstrap != null) {
bootstrap.shutdown();
}
this.bootstrap = new VotifierServerBootstrap(bind.getHostString(), bind.getPort(), this, v1Key == null);
this.bootstrap.start(error);
}
@Override
@@ -73,8 +59,13 @@ public class StandaloneVotifierPlugin implements VotifierPlugin, VoteHandler {
}
@Override
public String getVersion() {
return "2.3.5";
public LoggingAdapter getPluginLogger() {
return new JavaUtilLogger(Logger.getAnonymousLogger());
}
@Override
public VotifierScheduler getScheduler() {
return scheduler;
}
@Override
@@ -1,7 +1,12 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
import com.vexsoftware.votifier.util.KeyCreator;
import io.netty.channel.Channel;
import java.net.URL;
import java.nio.charset.StandardCharsets;
@@ -71,8 +76,13 @@ public class TestVotifierPlugin implements VotifierPlugin {
}
@Override
public String getVersion() {
return "2.3";
public LoggingAdapter getPluginLogger() {
return null;
}
@Override
public VotifierScheduler getScheduler() {
return null;
}
public void specificKeysOnly() {
@@ -84,4 +94,9 @@ public class TestVotifierPlugin implements VotifierPlugin {
keyMap.clear();
keyMap.put("default", KeyCreator.createKeyFrom("test"));
}
@Override
public void onVoteReceived(Channel channel, Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
}
}
@@ -1,6 +1,6 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.VotifierPlugin;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSA;
@@ -1,7 +1,7 @@
package com.vexsoftware.votifier.net.protocol;
import com.google.gson.JsonObject;
import com.vexsoftware.votifier.VotifierPlugin;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.util.GsonInst;
@@ -62,7 +62,7 @@ public class VotifierProtocol2DecoderTest {
}
@Test(expected = DecoderException.class)
public void testFailureDecodeBadPacket() throws Exception {
public void testFailureDecodeBadPacket() {
// Create a well-formed request
EmbeddedChannel channel = createChannel();
@@ -0,0 +1,49 @@
package com.vexsoftware.votifier.support.forwarding;
import com.google.common.collect.Lists;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class PMForwardingSinkTest {
@Test
public void testSuccessfulMultiDecode() throws Exception {
List<Vote> receivedVotes = Lists.newArrayList();
ForwardedVoteListener vl = receivedVotes::add;
List<Vote> sentVotes = Lists.newArrayList(
new Vote("serviceA", "usernameA", "1.1.1.1", "1546300800"),
new Vote("serviceB", "usernameBBBBBBB", "1.2.23.4", "1514764800", System.currentTimeMillis())
);
StringBuilder message = new StringBuilder();
for (Vote v : sentVotes) {
message.append(v.serialize());
}
byte[] messageBytes = message.toString().getBytes();
System.out.println(message.toString());
AbstractPluginMessagingForwardingSink sink = new AbstractPluginMessagingForwardingSink(vl) {
@Override
public void halt() {
}
};
sink.handlePluginMessage(messageBytes);
assertEquals(sentVotes.size(), receivedVotes.size());
for (int i = 0; i < receivedVotes.size(); i++) {
assertEquals(sentVotes.get(i), receivedVotes.get(i));
}
}
}
+1
View File
@@ -0,0 +1 @@
resources/
+22
View File
@@ -0,0 +1,22 @@
.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
@@ -0,0 +1,26 @@
## 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
@@ -0,0 +1,4 @@
logs/
modules/
locations.yml
+48
View File
@@ -0,0 +1,48 @@
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
@@ -0,0 +1,8 @@
version: 2
modules:
- jenkins://cmd_alert
- jenkins://cmd_find
- jenkins://cmd_list
- jenkins://cmd_send
- jenkins://cmd_server
- jenkins://reconnect_yaml
+1
View File
@@ -0,0 +1 @@
rsa/
+74
View File
@@ -0,0 +1,74 @@
# 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
@@ -0,0 +1 @@
../../../universal/build/libs/nuvotifier.jar
+1
View File
@@ -0,0 +1 @@
../resources/proxy.jar
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd $DIR
java -jar proxy.jar
+8
View File
@@ -0,0 +1,8 @@
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
Executable
+10
View File
@@ -0,0 +1,10 @@
#!/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
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,37 @@
# 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
@@ -0,0 +1,17 @@
# 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
@@ -0,0 +1,56 @@
# 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
@@ -0,0 +1 @@
../resources/paper.jar
+178
View File
@@ -0,0 +1,178 @@
# 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
@@ -0,0 +1 @@
rsa/
+29
View File
@@ -0,0 +1,29 @@
# 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
@@ -0,0 +1,7 @@
# 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
@@ -0,0 +1 @@
../s-run.sh
+39
View File
@@ -0,0 +1,39 @@
#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
@@ -0,0 +1,128 @@
# 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
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,37 @@
# 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
@@ -0,0 +1,17 @@
# 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
@@ -0,0 +1,56 @@
# 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
@@ -0,0 +1 @@
../resources/paper.jar
+178
View File
@@ -0,0 +1,178 @@
# 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
@@ -0,0 +1 @@
rsa/
+29
View File
@@ -0,0 +1,29 @@
# 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
@@ -0,0 +1,7 @@
# 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
@@ -0,0 +1 @@
../../../universal/build/libs/nuvotifier.jar
+1
View File
@@ -0,0 +1 @@
../s-run.sh
+39
View File
@@ -0,0 +1,39 @@
#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
@@ -0,0 +1,128 @@
# 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
@@ -0,0 +1,10 @@
# 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.
+5 -5
View File
@@ -30,9 +30,9 @@ following code snippets:
...
<repositories>
<repository>
<id>parallelblock-public</id>
<name>ParallelBlock Public Repository</name>
<url>https://repo.parallelblock.com/repository/maven-public/</url>
<id>ibj-public</id>
<name>Ichbinjoe Public Repository</name>
<url>https://nexus.ibj.io/repository/maven-public/</url>
</repository>
...
</repositories>
@@ -58,13 +58,13 @@ You can include NuVotifier into your gradle project using the following lines:
...
repositories {
maven {
url 'https://repo.parallelblock.com/repository/maven-public/'
url 'https://nexus.ibj.io/repository/maven-public/'
}
...
}
...
dependencies {
compileOnly "com.vexsoftware:nuvotifier-universal:verison"
compileOnly "com.vexsoftware:nuvotifier-universal:version"
...
}
...
+109
View File
@@ -0,0 +1,109 @@
# 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.
+4
View File
@@ -14,3 +14,7 @@ The developer guide for NuVotifier may be found [here](developer.md).
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).
+54 -41
View File
@@ -62,23 +62,23 @@ 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 (Bungeecord / Lilypad)
are using a Minecraft proxy (such as BungeeCord, LilyPad, or Velocity).
## Single Bungeecord Networks
## Single-Proxy BungeeCord/Velocity Networks
When running a single-bungeecord network, NuVotifier is able to operate as both
a Bungeecord vote receiver (emitting events for Bungeecord 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.
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 Bungeecord server. The Bungeecord server will act as the
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 instructions from the Bungeecord Nuvotifier for forwarding votes.
receive votes forwarded from the proxy's NuVotifier.
Like, the single server installation, you can download NuVotifier at any one of
Like the single server installation, you can download NuVotifier at any one of
the following locations:
+ [Github](https://github.com/parallelblock/NuVotifier/releases) - Official
@@ -98,30 +98,32 @@ correct.
### Plugin messaging based forwarding
This section describes how to set up plugin messaging forwarding. This will only
work correctly if you have one Bungeecord server, however may also work with
some exceptions in multi Bungeecord networks.
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`. 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!
`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 pluginMessaging!
plugin messaging votes. This will only work when using plugin messaging!
Since we have all of that configured, we can now move to the Bungeecord
NuVotifier configuration. It is once again located under `plugins/NuVotifier/`
directory (`config.yml`). 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 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 pluginMessaging based forwarding, lets set `method` under
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.
@@ -129,7 +131,7 @@ 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
it's default, `file`.
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
@@ -152,8 +154,14 @@ rather the vote be applied when the player next joins, set
until the player joins again.
By default, votes are saved for 10 days in the cache. This time can be increased
or decreased by changing the `voteTTL` field. This number can be as high or low
as you want. It is measured in days within the cache.
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
@@ -164,11 +172,11 @@ 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 Bungeecord plugin to perform the proper
forwarding logic (you will know if you need to do this).
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 Bungeecord servers acts as the head
vote receiver. This server will be the one you configure the voting websites to
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
@@ -178,18 +186,17 @@ 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
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 where to find the port number as we
will need it later to insert into the vote receiver Bungeecord 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
votifier 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.
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 Bungeecord server. The port
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
@@ -197,7 +204,7 @@ 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 Bungeecord NuVotifier
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
@@ -217,6 +224,12 @@ 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
Loaded 100 of 123 files, more files were not shown because too many files have changed in this diff. Show more