Compare commits

...
58 Commits
Author SHA1 Message Date
Andrew Steinborn d22f4cd58b NuVotifier 2.7.2 2021-01-26 13:23:46 -05:00
Andrew Steinborn 8ba7e87067 Some extra logging 2021-01-06 13:27:28 -05:00
Andrew Steinborn 5776acaf34 Merge pull request #221 from Janmm14/relocate-gson
Relocate gson
2021-01-06 12:52:56 -05:00
Andrew Steinborn 90f6ed8b55 Merge pull request #222 from Eufranio/master
Prevent NPE when the fallbackServer is null
2021-01-06 12:47:13 -05:00
Eufranio 616bd4ad01 Prevent NPE when the fallbackServer is null 2021-01-06 14:44:44 -03:00
Janmm14 448baf0329 Relocate gson 2021-01-06 16:45:30 +01:00
Andrew Steinborn 13fe852bcd Update dev network to 1.16.2 2020-09-03 15:52:17 -04:00
Andrew Steinborn c996e36b97 Don't re-register the raw data channel if it's already registered. Fixes #206 2020-08-14 14:36:01 -04:00
Andrew Steinborn ad9e6f5fe8 Do proper version replacement in Sponge/Velocity plugins using blossom 2020-07-13 13:22:24 -04:00
Andrew Steinborn 53eb9c8fec Update test network to 1.16.1 2020-07-07 22:18:49 -04:00
Andrew Steinborn 6dbda167d3 We support Bukkit 1.7.10 too. 2020-07-06 02:54:02 -04:00
Andrew Steinborn be9f92d5cd Bump back to 3.0.0-SNAPSHOT 2020-06-25 15:17:52 -04:00
Andrew Steinborn 96bf715fbc Bump to NuVotifier 2.7.1 2020-06-25 15:15:28 -04:00
Andrew Steinborn 628273413b Don't assume all channels will be NioSocketChannels. Fixes #196 2020-06-25 15:06:46 -04:00
Andrew Steinborn bbfa9e4aa9 Back to 3.0.0-SNAPSHOT 2020-06-24 22:39:54 -04:00
Andrew Steinborn a34b07f2d1 NuVotifier 2.7.0 2020-06-24 22:27:51 -04:00
Andrew Steinborn 1d90a59d5a Add more ArgsToVote tests. 2020-06-23 23:45:30 -04:00
Andrew Steinborn 2734836e58 Add a Sponge server to the testbed and fix one Sponge-related problem 2020-06-23 19:46:26 -04:00
Andrew Steinborn 768daf099f Make Velocity NuVotifier reload command consistent 2020-06-23 19:12:03 -04:00
Andrew Steinborn b6292667b8 Add Velocity testbed and fix some bugs in the Velocity port 2020-06-23 19:05:14 -04:00
Andrew Steinborn 8519535566 Do some test modernization, add some basic ArgsToVote tests 2020-06-23 18:46:58 -04:00
Andrew Steinborn 08e907678f Add additional debug output for pluginMessage forwarding 2020-06-23 18:25:27 -04:00
Andrew Steinborn 27b93ac045 Update dev environment to 1.15 2020-06-16 08:44:50 -04:00
Andrew Steinborn 97f4c91c24 Integrate SpotBugs and fix all errors. 2020-06-06 02:13:29 -04:00
Andrew Steinborn e919c845f7 Attempt to fix vote cache TTL
Instead of trying to expose this data by API, it is now internal to the vote caches.

The only substantial API change was the introduction of a copy constructor to the Vote object.
2020-06-04 00:06:09 -04:00
Andrew Steinborn 1fb35276f7 Support native reloading on BungeeCord, Sponge, and Velocity
/nvreload will remain supported to provide a consistent reload command for documentation, but it is now mostly only relevant for BungeeCord only.
2020-06-03 23:49:02 -04:00
Andrew Steinborn f5f596f21c Document that the Sponge event is fired sync now. 2020-06-03 23:37:41 -04:00
Andrew Steinborn 88523db213 Update issue templates 2020-05-30 23:53:06 -04:00
Andrew Steinborn 3599b0fa1b Restore compatibility with plugins that create Vote objects manually 2020-05-30 18:09:49 -04:00
Andrew Steinborn 950b543e98 Allow using up to 3 worker threads for NuVotifier 2020-05-14 01:38:24 -04:00
Andrew Steinborn e66927dd1a Add epoll transport support 2020-05-14 01:36:40 -04:00
Andrew Steinborn 98d4f69ffb Throttle errors and do general Netty optimizations
Closes #170
2020-05-14 00:58:18 -04:00
Andrew Steinborn ad44c76309 Fix build issues on Windows by just copying the file and not using a symbolic link 2020-05-11 14:24:39 -04:00
Andrew Steinborn 58de66699e Upgrade to Gradle 6.4 and resolve all deprecation issues. 2020-05-11 14:07:25 -04:00
Andrew Steinborn b81052327d Remove almost all use of Guava, closes #187
Guava has proven problematic on a number of occasions - it has caused issues with the Sponge port and there are incompatibilities with certain Forge-Bukkit hybrids. As we desire maximum compatibility, it is better simply to drop Guava altogether. Java 8+ already implements much of what was the bread-and-butter of Guava, or makes rolling our own implementation simpler.

The one place where Guava has been retained is in some configuration-loading code specific to Sponge, which can't be changed to use a non-Guava alternative.
2020-05-11 13:28:43 -04:00
Andrew Steinborn 59a3eff421 Clean up a little Netty code. 2020-05-08 16:50:13 -04:00
Andrew Steinborn 411a529e18 Notify users if they don't have plugins available to listen for votifier events on Bukkit. 2020-05-08 16:39:03 -04:00
Andrew Steinborn 4d6946ab2c Fix a typo 2020-05-08 16:29:20 -04:00
Andrew Steinborn d5ae57b28e Bump Netty to 4.1.49.Final
This could resolve (or greatly ameliorate) #170 but unverified.
2020-04-22 11:47:12 -04:00
Joe Hirschfeld aa1d9b3d71 [api] Add additional data field to the vote 2019-12-08 12:59:09 -08:00
Joe Hirschfeld c250048bf7 Remove localTimestamp field from Vote 2019-10-11 21:49:35 -07:00
Joe Hirschfeld 1db78a5bea Create new api module
Creates a new API module with stable api interfaces. Adds some unit
tests to the vote test, updates unit testing framework across the board.
2019-10-11 21:35:58 -07:00
Joe Hirschfeld 2ad0fa118f Update repositories in build.gradle 2019-10-11 20:43:05 -07:00
Joseph Hirschfeld 38afe59db0 Remove discord support link 2019-10-02 21:18:30 -07:00
Joseph Hirschfeld b173930c14 Add some much needed newlines! 2019-06-29 22:50:54 -07:00
Joseph Hirschfeld 61c4529df6 Update readme 2019-06-29 22:49:58 -07:00
Joseph Hirschfeld dd48af5b50 Move docs to github wiki page 2019-06-29 22:33:41 -07:00
Glare bc3c667d2a Fix a typo in the FAQ (#148) 2019-06-28 22:27:33 -07:00
Joseph Hirschfeld 1041bdb6b7 Add paypal donation link 2019-06-22 10:46:29 -04:00
Joseph Hirschfeld fc7a04547c Use bukkit ChatColor, not md5 ChatColor 2019-06-19 01:24:04 -04:00
Joseph Hirschfeld dd2a1c4ed5 Add /(p)testvote command 2019-06-19 01:20:07 -04:00
Joseph Hirschfeld c7667abf24 Remove bad singleton pattern use 2019-06-19 00:26:34 -04:00
Joseph Hirschfeld df484523c2 Add /nv(p)reload to all platforms
Also extract all existing platforms into a more modular command
interface
2019-06-19 00:23:14 -04:00
Joseph Hirschfeld 81fca5b218 Only expose the remote address string to handlers 2019-06-18 23:29:30 -04:00
Joseph Hirschfeld c520fd6650 Assume new cache when cachefile is empty 2019-06-17 10:33:46 -04:00
Andrew Steinborn e71ff635e3 Fix ConcurrentModificationException whilst sweeping votes (#141)
Fixes #139
2019-05-31 14:19:48 -04:00
Andrew Steinborn 4d52ef42e4 Fix plugin initialization on Sponge. (#142)
NOTE: In the future, should we support a platform that doesn't provide
Guava, we should simply remove the dependency from nuvotifier-common.
2019-05-31 13:25:16 -04:00
Joseph Hirschfeld c21d18594d Bump version to 3.0.0-SNAPSHOT 2019-04-27 00:21:50 -04:00
121 changed files with 2913 additions and 1494 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
custom: https://www.paypal.me/ibj
+73
View File
@@ -0,0 +1,73 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
<!--
Thank you for submitting an issue for NuVotifier. We appreciate
any and all bug reports.
Before you continue, please ensure you are reporting
a real bug and are not suffering from a configuration issue
or a plugin problem. Once NuVotifier has received and
called your vote listeners, it's not our issue any more.
If you are having difficulty setting up NuVotifier to receive
votes, you are in the wrong place. Please view our wiki
at https://github.com/NuVotifier/NuVotifier/wiki for a
comprehensive set of instructions for configuring and
diagnosing common problems with NuVotifier.
If you are certain that there is a problem with
NuVotifier, please continue filling the form below.
Make sure to follow these instructions:
1. Tell the exact NuVotifier version you're using.
If you just say "latest" your issue will get closed.
2. Please include all relevant logs and configuration
files. Make sure to redact any tokens in the config
file.
3. Don't write anything between these arrows.
We won't see it otherwise.
4. Before you create your issue, delete this block of
text and the code block at the very bottom. -->
### Information
**Platform on which you run NuVotifier**: <!-- Either Bukkit, Sponge, BungeeCord, or Velocity. -->
**Version of NuVotifier**:
```
<!--
Getting this depends on your platform:
Bukkit: Use /version Votifier
Sponge: Use /sponge plugins and find NuVotifier
BungeeCord: No such command exists.
Velocity: Use /velocity plugins from in-game and hover over "nuvotifier"
-->
```
**Relevant log**: <!-- Paste the log on https://gist.github.com/, replace this with link -->
### Details
**Description**
<!-- Replace this with a brief summary of the bug. -->
**Steps to reproduce**
<!-- Replace this with exactly what you did to cause the bug. -->
**Expected behavior**
<!-- Replace this with what you expected to happen. -->
```
Please make sure to delete this line of text and the three ``` characters surrounding it, or your issue will be closed.
```
+14 -24
View File
@@ -1,35 +1,25 @@
# NuVotifier
NuVotifier is a Bukkit plugin whose purpose is to be notified (aka *votified*)
when a vote is made on a Minecraft server top list for the server. NuVotifier
creates a *lightweight* server that waits for connections by Minecraft server
lists and uses a simple protocol to get the required information. NuVotifier is
*secure*, and makes sure that all vote notifications are delivered by authentic
top lists.
NuVotifier is a secure alternative to using the original Votifier project.
NuVotifier will work in place of Votifier - any vote listener that supports
Votifier will also support NuVotifier.
## Why fork Votifier?
NuVotifier supports many different platforms using the same universal JAR:
Votifier has lagged in activity, has severe security flaws in its protocol, and
is vulnerable to denial of service attacks.
+ Bukkit / Spigot / Paper >=1.7.10
+ Sponge 7
+ BungeeCord / Waterfall
+ Velocity
## Is NuVotifier a drop-in replacement for Votifier?
NuVotifier also adds forwarding and listener test commands not present in the
original version.
Yes! Except for some minor non-breaking changes (look into the [technical qa
docs](docs/technical_qa.md) for more info), NuVotifier is 100% compatible with
the old Votifier with many more features.
[Setup Guide](https://github.com/NuVotifier/NuVotifier/wiki/Setup-Guide)
# Documentation / Configuration Guide
[Troubleshooting Guide](https://github.com/NuVotifier/NuVotifier/wiki/Troubleshooting-Guide)
All documentation is located under the [docs](docs/) directory. A server admin
configuration guide is available [here](docs/usage.md).
# Support
Feel free to join the MOSS Discord server using the below link:
[![Discord](https://i.imgur.com/HLPoNnY.png)](https://discord.gg/anUtuAC)
[Developer Information](https://github.com/NuVotifier/NuVotifier/wiki/Developer-Documentation)
# License
NuVotifier is GNU GPLv3 licensed. This project's license can be viewed
[here](LICENSE).
NuVotifier is GNU GPLv3 licensed. This project's license can be viewed [here](LICENSE).
+4
View File
@@ -0,0 +1,4 @@
# nuvotifier-api
Files in this directory have a strong API promise - they really shouldn't
change over time. Feel free to depend against it!
+3
View File
@@ -0,0 +1,3 @@
dependencies {
implementation 'com.google.code.gson:gson:2.8.0'
}
@@ -21,6 +21,9 @@ package com.vexsoftware.votifier.model;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.Arrays;
import java.util.Base64;
/**
* A model for a vote.
*
@@ -48,26 +51,31 @@ public class Vote {
*/
private String timeStamp;
/**
* Timestamp (unix-millis) normalized and taken from a known source
*/
private final long localTimestamp;
private byte[] additionalData;
@Deprecated
public Vote() {
localTimestamp = System.currentTimeMillis();
}
public Vote(String serviceName, String username, String address, String timeStamp) {
this(serviceName, username, address, timeStamp, System.currentTimeMillis());
}
public Vote(String serviceName, String username, String address, String timeStamp, long localTimestamp) {
this.serviceName = serviceName;
this.username = username;
this.address = address;
this.timeStamp = timeStamp;
this.localTimestamp = localTimestamp;
this.additionalData = null;
}
public Vote(String serviceName, String username, String address, String timeStamp, byte[] additionalData) {
this.serviceName = serviceName;
this.username = username;
this.address = address;
this.timeStamp = timeStamp;
this.additionalData = additionalData == null ? null : additionalData.clone();
}
public Vote(Vote vote) {
this(vote.getServiceName(), vote.getUsername(), vote.getAddress(), vote.getTimeStamp(),
vote.getAdditionalData() == null ? null : vote.getAdditionalData().clone());
}
private static String getTimestamp(JsonElement object) {
@@ -82,16 +90,22 @@ public class Vote {
this(jsonObject.get("serviceName").getAsString(),
jsonObject.get("username").getAsString(),
jsonObject.get("address").getAsString(),
getTimestamp(jsonObject.get("timestamp")),
// maintained for backwards compatibility with <2.3.6 peers
(jsonObject.has("localTimestamp") ? jsonObject.get("localTimestamp").getAsLong() : System.currentTimeMillis()));
getTimestamp(jsonObject.get("timestamp")));
if (jsonObject.has("additionalData"))
additionalData = Base64.getDecoder().decode(jsonObject.get("additionalData").getAsString());
}
@Override
public String toString() {
String data;
if (additionalData == null)
data = "null";
else
data = Base64.getEncoder().encodeToString(additionalData);
return "Vote (from:" + serviceName + " username:" + username
+ " address:" + address + " timeStamp:" + timeStamp
+ " localTimestamp:" + localTimestamp + ")";
+ " additionalData:" + data + ")";
}
/**
@@ -171,12 +185,12 @@ public class Vote {
}
/**
* Gets the local timestamp, in unix-millis. Calculated locally by a NuVotifier instance
* Returns additional data sent with the vote, if it exists.
*
* @return The local timestamp
* @return Additional data sent with the vote
*/
public long getLocalTimestamp() {
return localTimestamp;
public byte[] getAdditionalData() {
return additionalData == null ? null : additionalData.clone();
}
public JsonObject serialize() {
@@ -185,21 +199,23 @@ public class Vote {
ret.addProperty("username", username);
ret.addProperty("address", address);
ret.addProperty("timestamp", timeStamp);
ret.addProperty("localTimestamp", localTimestamp);
if (additionalData != null)
ret.addProperty("additionalData", Base64.getEncoder().encodeToString(additionalData));
return ret;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!(o instanceof Vote)) return false;
Vote vote = (Vote) o;
if (!serviceName.equals(vote.serviceName)) return false;
if (!username.equals(vote.username)) return false;
if (!address.equals(vote.address)) return false;
return timeStamp.equals(vote.timeStamp);
if (!timeStamp.equals(vote.timeStamp)) return false;
return Arrays.equals(additionalData, vote.additionalData);
}
@Override
@@ -0,0 +1,77 @@
package com.vexsoftware.votifier.model;
import com.google.gson.JsonObject;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class VoteTest {
@Test
public void testVoteConstructFromJsonObject() {
String serviceName = "serviceNameA";
String username = "usernameA";
String address = "home";
String timestamp = "1234";
JsonObject o = new JsonObject();
o.addProperty("serviceName", serviceName);
o.addProperty("username", username);
o.addProperty("address", address);
o.addProperty("timestamp", timestamp);
Vote v = new Vote(o);
assertEquals(serviceName, v.getServiceName());
assertEquals(username, v.getUsername());
assertEquals(address, v.getAddress());
assertEquals(timestamp, v.getTimeStamp());
}
@Test
public void testVoteConstructFromJsonObjectLongTS() {
String serviceName = "serviceNameA";
String username = "usernameA";
String address = "home";
JsonObject o = new JsonObject();
o.addProperty("serviceName", serviceName);
o.addProperty("username", username);
o.addProperty("address", address);
o.addProperty("timestamp", 1234);
Vote v = new Vote(o);
assertEquals(serviceName, v.getServiceName());
assertEquals(username, v.getUsername());
assertEquals(address, v.getAddress());
assertEquals("1234", v.getTimeStamp());
}
@Test
public void testVoteSerialize() {
Vote v = new Vote("service", "username", "address", "1234");
JsonObject o = v.serialize();
assertEquals(4, o.size());
assertEquals("service", o.get("serviceName").getAsString());
assertEquals("username", o.get("username").getAsString());
assertEquals("address", o.get("address").getAsString());
assertEquals("1234", o.get("timestamp").getAsString());
}
@Test
public void testVoteEquals() {
Vote v1 = new Vote("service", "username", "address", "1234");
assertEquals(v1, v1);
assertEquals(v1.hashCode(), new Vote("service", "username", "address", "1234").hashCode());
assertEquals(v1, new Vote("service", "username", "address", "1234"));
assertNotEquals(v1, new Object());
assertNotEquals(v1, null);
assertNotEquals(v1, new Vote("service2", "username", "address", "1234"));
assertNotEquals(v1, new Vote("service", "username1", "address", "1234"));
assertNotEquals(v1, new Vote("service", "username", "address1", "1234"));
assertNotEquals(v1, new Vote("service", "username", "address", "12345"));
}
}
+37 -5
View File
@@ -1,28 +1,60 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id "com.github.spotbugs" version "4.2.4" apply false
id "net.kyori.blossom" version "1.1.0" apply false
}
allprojects {
group 'com.vexsoftware'
version '2.5.2'
version '2.7.2'
}
subprojects {
apply plugin: 'java'
apply plugin: "com.github.spotbugs"
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven {
url 'https://nexus.ibj.io/repository/maven-public/'
url "https://hub.spigotmc.org/nexus/content/repositories/snapshots"
}
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
maven {
url "https://repo.spongepowered.org/maven"
}
maven {
url "https://repo.velocitypowered.com/snapshots/"
}
}
dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:5.4.2"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.4.2"
testImplementation "org.junit.jupiter:junit-jupiter-params:5.4.2"
testImplementation "org.mockito:mockito-core:2.+"
}
processResources {
filter(ReplaceTokens, tokens: ["app.version": this.project.version])
}
}
if (System.env.PUBLISH) {
apply from: 'publish.gradle'
test {
useJUnitPlatform()
}
tasks.withType(com.github.spotbugs.snom.SpotBugsTask) {
reports {
html {
enabled = true
destination = file("$buildDir/reports/spotbugs/main/spotbugs.html")
stylesheet = 'fancy-hist.xsl'
}
}
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
dependencies {
compileOnly group: "org.spigotmc", name: "spigot-api", version: "1.8.8-R0.1-SNAPSHOT"
compile project(":nuvotifier-common")
implementation project(":nuvotifier-api")
implementation project(":nuvotifier-common")
}
@@ -18,8 +18,8 @@
package com.vexsoftware.votifier;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.vexsoftware.votifier.cmd.NVReloadCmd;
import com.vexsoftware.votifier.cmd.TestVoteCmd;
import com.vexsoftware.votifier.forwarding.BukkitPluginMessagingForwardingSink;
import com.vexsoftware.votifier.support.forwarding.ForwardedVoteListener;
import com.vexsoftware.votifier.support.forwarding.ForwardingVoteSink;
@@ -33,20 +33,20 @@ 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.IOUtil;
import com.vexsoftware.votifier.util.KeyCreator;
import com.vexsoftware.votifier.util.TokenUtil;
import io.netty.channel.Channel;
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.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.Key;
import java.security.KeyPair;
import java.util.HashMap;
@@ -61,16 +61,6 @@ import java.util.logging.Level;
*/
public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, VotifierPlugin, ForwardedVoteListener {
/**
* The Votifier instance.
*/
private static NuVotifierBukkit instance;
/**
* The current Votifier version.
*/
private String version;
/**
* The server bootstrap.
*/
@@ -99,7 +89,9 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
scheduler = new BukkitScheduler(this);
pluginLogger = new JavaUtilLogger(getLogger());
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
if (!getDataFolder().mkdir()) {
throw new RuntimeException("Unable to create the plugin data folder " + getDataFolder());
}
}
// Handle configuration.
@@ -124,13 +116,15 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
getLogger().info("Configuring Votifier for the first time...");
// Initialize the configuration file.
config.createNewFile();
if (!config.createNewFile()) {
throw new IOException("Unable to create the config file at " + config);
}
// Load and manually replace variables in the configuration.
String cfgStr = new String(ByteStreams.toByteArray(getResource("bukkitConfig.yml")), StandardCharsets.UTF_8);
String cfgStr = new String(IOUtil.readAllBytes(getResource("bukkitConfig.yml")), StandardCharsets.UTF_8);
String token = TokenUtil.newToken();
cfgStr = cfgStr.replace("%default_token%", token).replace("%ip%", hostAddr);
Files.asCharSink(config, StandardCharsets.UTF_8).write(cfgStr);
Files.copy(new ByteArrayInputStream(cfgStr.getBytes(StandardCharsets.UTF_8)), config.toPath(), StandardCopyOption.REPLACE_EXISTING);
/*
* Remind hosted server admins to be sure they have the right
@@ -164,7 +158,9 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
*/
try {
if (!rsaDirectory.exists()) {
rsaDirectory.mkdir();
if (!rsaDirectory.mkdir()) {
throw new RuntimeException("Unable to create the RSA key folder " + rsaDirectory);
}
keyPair = RSAKeygen.generate(2048);
RSAIO.save(rsaDirectory, keyPair);
} else {
@@ -273,10 +269,8 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
@Override
public void onEnable() {
NuVotifierBukkit.instance = this;
// Set the plugin version.
version = getDescription().getVersion();
getCommand("nvreload").setExecutor(new NVReloadCmd(this));
getCommand("testvote").setExecutor(new TestVoteCmd(this));
if (!loadAndBind()) {
gracefulExit();
@@ -290,17 +284,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
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() {
public boolean reload() {
try {
halt();
} catch (Exception ex) {
@@ -309,6 +293,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
if (loadAndBind()) {
getLogger().info("Reload was successful.");
return true;
} else {
try {
halt();
@@ -316,6 +301,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "On reload, there was a problem loading, and we could not re-halt the server. Votifier is in an unstable state!", ex);
}
return false;
}
}
@@ -323,15 +309,6 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
getLogger().log(Level.SEVERE, "Votifier did not initialize properly!");
}
/**
* Gets the instance.
*
* @return The instance
*/
public static NuVotifierBukkit getInstance() {
return instance;
}
@Override
public LoggingAdapter getPluginLogger() {
return pluginLogger;
@@ -357,28 +334,24 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
}
@Override
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) {
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
getLogger().info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
} else {
getLogger().info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
}
getLogger().info("Got a " + protocolVersion.humanReadable + " vote record from " + remoteAddress + " -> " + vote);
}
Bukkit.getScheduler().runTask(this, () -> Bukkit.getPluginManager().callEvent(new VotifierEvent(vote)));
Bukkit.getScheduler().runTask(this, () -> fireVotifierEvent(vote));
}
@Override
public void onError(Channel channel, boolean alreadyHandledVote, Throwable throwable) {
public void onError(Throwable throwable, boolean alreadyHandledVote, String remoteAddress) {
if (debug) {
if (alreadyHandledVote) {
getLogger().log(Level.SEVERE, "Vote processed, however an exception " +
"occurred with a vote from " + channel.remoteAddress(), throwable);
"occurred with a vote from " + remoteAddress, throwable);
} else {
getLogger().log(Level.SEVERE, "Unable to process vote from " + channel.remoteAddress(), throwable);
getLogger().log(Level.SEVERE, "Unable to process vote from " + remoteAddress, throwable);
}
} else if (!alreadyHandledVote) {
getLogger().log(Level.SEVERE, "Unable to process vote from " + channel.remoteAddress());
getLogger().log(Level.SEVERE, "Unable to process vote from " + remoteAddress);
}
}
@@ -387,6 +360,15 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
if (debug) {
getLogger().info("Got a forwarded vote -> " + v);
}
Bukkit.getScheduler().runTask(this, () -> Bukkit.getPluginManager().callEvent(new VotifierEvent(v)));
Bukkit.getScheduler().runTask(this, () -> fireVotifierEvent(v));
}
private void fireVotifierEvent(Vote vote) {
if (VotifierEvent.getHandlerList().getRegisteredListeners().length == 0) {
getLogger().log(Level.SEVERE, "A vote was received, but you don't have any listeners available to listen for it.");
getLogger().log(Level.SEVERE, "See https://github.com/NuVotifier/NuVotifier/wiki/Setup-Guide#vote-listeners for");
getLogger().log(Level.SEVERE, "a list of listeners you can configure.");
}
Bukkit.getPluginManager().callEvent(new VotifierEvent(vote));
}
}
@@ -0,0 +1,31 @@
package com.vexsoftware.votifier.cmd;
import com.vexsoftware.votifier.NuVotifierBukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class NVReloadCmd implements CommandExecutor {
private final NuVotifierBukkit plugin;
public NVReloadCmd(NuVotifierBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender.hasPermission("nuvotifier.reload")) {
sender.sendMessage(ChatColor.GRAY + "Reloading NuVotifier...");
if (plugin.reload()) {
sender.sendMessage(ChatColor.DARK_GREEN + "NuVotifier has been reloaded!");
} else {
sender.sendMessage(ChatColor.DARK_RED + "Looks like there was a problem reloading NuVotifier, check the console!");
}
} else {
sender.sendMessage(ChatColor.DARK_RED + "You do not have permission to do this!");
}
return true;
}
}
@@ -0,0 +1,40 @@
package com.vexsoftware.votifier.cmd;
import com.vexsoftware.votifier.NuVotifierBukkit;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.util.ArgsToVote;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class TestVoteCmd implements CommandExecutor {
private final NuVotifierBukkit plugin;
public TestVoteCmd(NuVotifierBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender.hasPermission("nuvotifier.testvote")) {
Vote v;
try {
v = ArgsToVote.parse(args);
} catch (IllegalArgumentException e) {
sender.sendMessage(ChatColor.DARK_RED + "Error while parsing arguments to create test vote: " + e.getMessage());
sender.sendMessage(ChatColor.GRAY + "Usage hint: /testvote [username] [serviceName=?] [username=?] [address=?] [localTimestamp=?] [timestamp=?]");
return true;
}
plugin.onVoteReceived(v, VotifierSession.ProtocolVersion.TEST, "localhost.test");
sender.sendMessage(ChatColor.GREEN + "Test vote executed: " + v.toString());
} else {
sender.sendMessage(ChatColor.DARK_RED + "You do not have permission to do this!");
}
return true;
}
}
@@ -37,7 +37,7 @@ public class BukkitPluginMessagingForwardingSink extends AbstractPluginMessaging
try {
this.handlePluginMessage(bytes);
} catch (Exception e) {
NuVotifierBukkit.getInstance().getLogger().log(Level.SEVERE, "There was an unknown error when processing a forwarded vote.", e);
p.getLogger().log(Level.SEVERE, "There was an unknown error when processing a forwarded vote.", e);
}
}
}
+15 -2
View File
@@ -8,6 +8,19 @@ api-version: 1.13
commands:
nvreload:
description: Reloads the NuVotifier configuration
permission: nuvotifier.console
permission: nuvotifier.reload
permission-message: "You do not have permission to run this command."
usage: "/nvreload"
usage: "/nvreload"
testvote:
description: Sends a test vote to the server
permission: nuvotifier.testvote
permission-message: "You do not have permission to run this command."
usage: "/testvote [username] [serviceName=?] [username=?] [address=?] [localTimestamp=?] [timestamp=?]"
permissions:
nuvotifier.reload:
description: Allows you to reload the NuVotifier plugin
default: op
nuvotifier.testvote:
description: Allows you to send a test vote
default: op
+2 -1
View File
@@ -1,4 +1,5 @@
dependencies {
compileOnly group: "net.md-5", name: "bungeecord-api", version: "1.8-SNAPSHOT"
compile project(":nuvotifier-common")
implementation project(":nuvotifier-api")
implementation project(":nuvotifier-common")
}
@@ -1,14 +1,15 @@
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;
import java.util.Objects;
public class BungeeBackendServer implements BackendServer {
private final ServerInfo info;
public BungeeBackendServer(ServerInfo info) {
this.info = Preconditions.checkNotNull(info, "info");
this.info = Objects.requireNonNull(info, "info");
}
@Override
@@ -1,9 +1,9 @@
package com.vexsoftware.votifier.bungee;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.common.collect.ImmutableList;
import com.vexsoftware.votifier.VoteHandler;
import com.vexsoftware.votifier.bungee.cmd.NVReloadCmd;
import com.vexsoftware.votifier.bungee.cmd.TestVoteCmd;
import com.vexsoftware.votifier.net.VotifierServerBootstrap;
import com.vexsoftware.votifier.platform.BackendServer;
import com.vexsoftware.votifier.platform.JavaUtilLogger;
@@ -21,26 +21,26 @@ import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAIO;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAKeygen;
import com.vexsoftware.votifier.util.IOUtil;
import com.vexsoftware.votifier.util.KeyCreator;
import com.vexsoftware.votifier.util.TokenUtil;
import io.netty.channel.Channel;
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.api.plugin.PluginManager;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.Key;
import java.security.KeyPair;
import java.util.*;
@@ -81,10 +81,10 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
private LoggingAdapter pluginLogger;
private void loadAndBind() {
scheduler = new BungeeScheduler(this);
pluginLogger = new JavaUtilLogger(getLogger());
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
if (!getDataFolder().mkdir()) {
throw new RuntimeException("Unable to create the plugin data folder " + getDataFolder());
}
}
// Handle configuration.
@@ -98,12 +98,14 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
getLogger().info("Configuring Votifier for the first time...");
// Initialize the configuration file.
config.createNewFile();
if (!config.createNewFile()) {
throw new IOException("Unable to create the config file at " + config);
}
String cfgStr = new String(ByteStreams.toByteArray(getResourceAsStream("bungeeConfig.yml")), StandardCharsets.UTF_8);
String cfgStr = new String(IOUtil.readAllBytes(getResourceAsStream("bungeeConfig.yml")), StandardCharsets.UTF_8);
String token = TokenUtil.newToken();
cfgStr = cfgStr.replace("%default_token%", token);
Files.asCharSink(config, StandardCharsets.UTF_8).write(cfgStr);
Files.copy(new ByteArrayInputStream(cfgStr.getBytes(StandardCharsets.UTF_8)), config.toPath(), StandardCopyOption.REPLACE_EXISTING);
/*
* Remind hosted server admins to be sure they have the right
@@ -141,7 +143,9 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
*/
try {
if (!rsaDirectory.exists()) {
rsaDirectory.mkdir();
if (!rsaDirectory.mkdir()) {
throw new RuntimeException("Unable to create the RSA key folder " + rsaDirectory);
}
keyPair = RSAKeygen.generate(2048);
RSAIO.save(rsaDirectory, keyPair);
} else {
@@ -161,7 +165,7 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
}
} else {
String token = TokenUtil.newToken();
configuration.set("tokens", ImmutableMap.of("default", token));
configuration.set("tokens", Collections.singletonMap("default", token));
tokens.put("default", KeyCreator.createKeyFrom(token));
try {
ConfigurationProvider.getProvider(YamlConfiguration.class).save(configuration, config);
@@ -220,13 +224,13 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
getLogger().info("Vote cache none selected for caching: votes that cannot be immediately delivered will be lost.");
} else if ("memory".equals(cacheMethod)) {
voteCache = new MemoryVoteCache(
ProxyServer.getInstance().getServers().size(), this,
this,
fwdCfg.getInt("pluginMessaging.memory.cacheTime", -1));
getLogger().info("Using in-memory cache for votes that are not able to be delivered.");
} else if ("file".equals(cacheMethod)) {
try {
voteCache = new FileVoteCache(
ProxyServer.getInstance().getServers().size(), this,
this,
new File(getDataFolder(), fwdCfg.getString("pluginMessaging.file.name")),
fwdCfg.getInt("pluginMessaging.file.cacheTime", -1));
} catch (IOException e) {
@@ -253,6 +257,7 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
String fallbackServer = fwdCfg.getString("pluginMessaging.joinedServerFallback", null);
if (fallbackServer != null && fallbackServer.isEmpty()) fallbackServer = null;
forwardingMethod = new OnlineForwardPluginMessagingForwardingSource(channel, this, filter, voteCache, fallbackServer, dumpRate);
getLogger().info("Forwarding votes over PluginMessaging channel '" + channel + "' for vote forwarding for online players!");
} catch (RuntimeException e) {
getLogger().log(Level.SEVERE, "NuVotifier could not set up PluginMessaging for vote forwarding!", e);
}
@@ -287,7 +292,7 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
}
}
forwardingMethod = new ProxyForwardingVoteSource(this, bootstrap::client, serverList, null);
forwardingMethod = bootstrap.createForwardingSource(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.");
@@ -296,17 +301,15 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
@Override
public void onEnable() {
loadAndBind();
scheduler = new BungeeScheduler(this);
pluginLogger = new JavaUtilLogger(getLogger());
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();
}
});
PluginManager pm = ProxyServer.getInstance().getPluginManager();
pm.registerCommand(this, new NVReloadCmd(this));
pm.registerCommand(this, new TestVoteCmd(this));
pm.registerListener(this, new ReloadListener(this));
loadAndBind();
}
private void halt() {
@@ -322,7 +325,7 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
}
}
public void reload() {
public boolean reload() {
try {
halt();
} catch (Exception ex) {
@@ -332,6 +335,7 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
try {
loadAndBind();
getLogger().info("Reload was successful.");
return true;
} catch (Exception ex) {
try {
halt();
@@ -340,6 +344,7 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
getLogger().log(Level.SEVERE, "On reload, there was a problem loading, and we could not re-halt the server. Votifier is in an unstable state!", ex);
getLogger().log(Level.SEVERE, "(halt exception)", ex2);
}
return false;
}
}
@@ -350,12 +355,12 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
}
@Override
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) {
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
getLogger().info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
getLogger().info("Got a protocol v1 vote record from " + remoteAddress + " -> " + vote);
} else {
getLogger().info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
getLogger().info("Got a protocol v2 vote record from " + remoteAddress + " -> " + vote);
}
}
@@ -367,16 +372,16 @@ public class NuVotifier extends Plugin implements VoteHandler, ProxyVotifierPlug
}
@Override
public void onError(Channel channel, boolean alreadyHandledVote, Throwable throwable) {
public void onError(Throwable throwable, boolean alreadyHandledVote, String remoteAddress) {
if (debug) {
if (alreadyHandledVote) {
getLogger().log(Level.SEVERE, "Vote processed, however an exception " +
"occurred with a vote from " + channel.remoteAddress(), throwable);
"occurred with a vote from " + remoteAddress, throwable);
} else {
getLogger().log(Level.SEVERE, "Unable to process vote from " + channel.remoteAddress(), throwable);
getLogger().log(Level.SEVERE, "Unable to process vote from " + remoteAddress, throwable);
}
} else if (!alreadyHandledVote) {
getLogger().log(Level.SEVERE, "Unable to process vote from " + channel.remoteAddress());
getLogger().log(Level.SEVERE, "Unable to process vote from " + remoteAddress);
}
}
@@ -33,6 +33,9 @@ public final class OnlineForwardPluginMessagingForwardingSource extends Abstract
if (p != null && p.getServer() != null &&
serverFilter.isAllowed(p.getServer().getInfo().getName())) {
if (forwardSpecific(new BungeeBackendServer(p.getServer().getInfo()), v)) {
if (plugin.isDebug()) {
plugin.getPluginLogger().info("Successfully forwarded vote " + v + " to server " + p.getServer().getInfo().getName());
}
return;
}
}
@@ -0,0 +1,18 @@
package com.vexsoftware.votifier.bungee;
import net.md_5.bungee.api.event.ProxyReloadEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
public class ReloadListener implements Listener {
private final NuVotifier nuVotifier;
public ReloadListener(NuVotifier nuVotifier) {
this.nuVotifier = nuVotifier;
}
@EventHandler
public void onProxyReload(ProxyReloadEvent event) {
this.nuVotifier.reload();
}
}
@@ -0,0 +1,44 @@
package com.vexsoftware.votifier.bungee.cmd;
import com.vexsoftware.votifier.bungee.NuVotifier;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class NVReloadCmd extends Command {
private final NuVotifier plugin;
private static final BaseComponent reloadingNuVotifier = new TextComponent("Reloading NuVotifier...");
private static final BaseComponent nuvotifierHasBeenReloaded = new TextComponent("NuVotifier has been reloaded!");
private static final BaseComponent problem = new TextComponent("Looks like there was a problem reloading NuVotifier, check the console!");
private static final BaseComponent permission = new TextComponent("You do not have permission to do this!");
static {
reloadingNuVotifier.setColor(ChatColor.GRAY);
nuvotifierHasBeenReloaded.setColor(ChatColor.DARK_GREEN);
problem.setColor(ChatColor.DARK_RED);
permission.setColor(ChatColor.DARK_RED);
}
public NVReloadCmd(NuVotifier plugin) {
super("pnvreload", "nuvotifier.reload");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (sender.hasPermission("nuvotifier.reload")) {
sender.sendMessage(reloadingNuVotifier);
if (plugin.reload()) {
sender.sendMessage(nuvotifierHasBeenReloaded);
} else {
sender.sendMessage(problem);
}
} else {
sender.sendMessage(permission);
}
}
}
@@ -0,0 +1,51 @@
package com.vexsoftware.votifier.bungee.cmd;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.util.ArgsToVote;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class TestVoteCmd extends Command {
private final NuVotifier plugin;
private static final BaseComponent permission = new TextComponent("You do not have permission to do this!");
private static final BaseComponent usage = new TextComponent("Usage hint: /ptestvote [username] [serviceName=?] [username=?] [address=?] [localTimestamp=?] [timestamp=?]");
static {
usage.setColor(ChatColor.GRAY);
}
public TestVoteCmd(NuVotifier plugin) {
super("ptestvote", "nuvotifier.testvote");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (sender.hasPermission("nuvotifier.testvote")) {
Vote v;
try {
v = ArgsToVote.parse(args);
} catch (IllegalArgumentException e) {
TextComponent c = new TextComponent("Error while parsing arguments to create test vote: " + e.getMessage());
c.setColor(ChatColor.DARK_RED);
sender.sendMessage(c);
sender.sendMessage(usage);
return;
}
plugin.onVoteReceived(v, VotifierSession.ProtocolVersion.TEST, "localhost.test");
TextComponent c = new TextComponent("Test vote executed: " + v.toString());
c.setColor(ChatColor.GREEN);
sender.sendMessage(c);
} else {
sender.sendMessage(permission);
}
}
}
+6 -5
View File
@@ -1,7 +1,8 @@
dependencies {
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
implementation project(":nuvotifier-api")
implementation group: "io.netty", name: "netty-handler", version: "4.1.49.Final"
implementation group: "io.netty", name: "netty-transport-native-epoll", version: "4.1.49.Final", classifier: "linux-x86_64"
implementation 'com.google.code.gson:gson:2.8.0'
testImplementation group: "org.json", name: "json", version: "20180130" // retain this for testing reasons
testImplementation 'com.google.guava:guava:28.1-jre'
}
@@ -5,16 +5,11 @@ import com.vexsoftware.votifier.net.VotifierSession;
import io.netty.channel.Channel;
public interface VoteHandler {
void onVoteReceived(Channel channel, Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception;
// these default members are kept for compatibility sake - there probably isn't anyone out there implementing these
// on their own, but we still deprecate them before removing them because we are nice!
default void onError(Channel channel, boolean voteAlreadyCompleted, Throwable throwable) {
onError(channel, throwable);
}
void onVoteReceived(Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) throws Exception;
@Deprecated
default void onError(Channel channel, Throwable throwable) {
default void onError(Throwable throwable, boolean voteAlreadyCompleted, String remoteAddress) {
throw new RuntimeException("Unimplemented onError handler");
}
}
@@ -1,29 +1,41 @@
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 com.vexsoftware.votifier.support.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.support.forwarding.proxy.ProxyForwardingVoteSource;
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.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.FastThreadLocalThread;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadFactory;
import java.util.function.Consumer;
public class VotifierServerBootstrap {
private static final boolean USE_EPOLL = Epoll.isAvailable();
private final String host;
private final int port;
private final NioEventLoopGroup bossLoopGroup;
private final NioEventLoopGroup eventLoopGroup;
private final EventLoopGroup bossLoopGroup;
private final EventLoopGroup eventLoopGroup;
private final VotifierPlugin plugin;
private final boolean v1Disable;
@@ -34,23 +46,41 @@ public class VotifierServerBootstrap {
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());
if (USE_EPOLL) {
this.bossLoopGroup = new EpollEventLoopGroup(1, createThreadFactory("Votifier epoll boss"));
this.eventLoopGroup = new EpollEventLoopGroup(3, createThreadFactory("Votifier epoll worker"));
plugin.getPluginLogger().info("Using epoll transport to accept votes.");
} else {
this.bossLoopGroup = new NioEventLoopGroup(1, createThreadFactory("Votifier NIO boss"));
this.eventLoopGroup = new NioEventLoopGroup(3, createThreadFactory("Votifier NIO worker"));
plugin.getPluginLogger().info("Using NIO transport to accept votes.");
}
}
private static ThreadFactory createThreadFactory(String name) {
return runnable -> {
FastThreadLocalThread thread = new FastThreadLocalThread(runnable, name);
thread.setDaemon(true);
return thread;
};
}
public void start(Consumer<Throwable> error) {
Objects.requireNonNull(error, "error");
VoteInboundHandler voteInboundHandler = new VoteInboundHandler(plugin);
new ServerBootstrap()
.channel(NioServerSocketChannel.class)
.channel(USE_EPOLL ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
.group(bossLoopGroup, eventLoopGroup)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) {
protected void initChannel(SocketChannel channel) {
channel.attr(VotifierSession.KEY).set(new VotifierSession());
channel.attr(VotifierPlugin.KEY).set(plugin);
channel.pipeline().addLast("greetingHandler", new VotifierGreetingHandler());
channel.pipeline().addLast("greetingHandler", VotifierGreetingHandler.INSTANCE);
channel.pipeline().addLast("protocolDifferentiator", new VotifierProtocolDifferentiator(false, !v1Disable));
channel.pipeline().addLast("voteHandler", new VoteInboundHandler(plugin));
channel.pipeline().addLast("voteHandler", voteInboundHandler);
}
})
.bind(host, port)
@@ -70,12 +100,17 @@ public class VotifierServerBootstrap {
});
}
public Bootstrap client() {
private Bootstrap client() {
return new Bootstrap()
.channel(NioSocketChannel.class)
.channel(USE_EPOLL ? EpollSocketChannel.class : NioSocketChannel.class)
.group(eventLoopGroup);
}
public ProxyForwardingVoteSource createForwardingSource(List<ProxyForwardingVoteSource.BackendServer> backendServers,
VoteCache voteCache) {
return new ProxyForwardingVoteSource(plugin, this::client, backendServers, voteCache);
}
public void shutdown() {
if (serverChannel != null) {
try {
@@ -40,8 +40,14 @@ public class VotifierSession {
}
public enum ProtocolVersion {
UNKNOWN,
ONE,
TWO
UNKNOWN("unknown"),
ONE("protocol v1"),
TWO("protocol v2"),
TEST("test");
public final String humanReadable;
ProtocolVersion(String hr) {
this.humanReadable = hr;
}
}
}
@@ -6,21 +6,29 @@ import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.util.GsonInst;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.concurrent.atomic.AtomicLong;
@ChannelHandler.Sharable
public class VoteInboundHandler extends SimpleChannelInboundHandler<Vote> {
private final VoteHandler handler;
private final AtomicLong lastError;
private final AtomicLong errorsSent;
public VoteInboundHandler(VoteHandler handler) {
this.handler = handler;
this.lastError = new AtomicLong();
this.errorsSent = new AtomicLong();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, final Vote vote) throws Exception {
VotifierSession session = ctx.channel().attr(VotifierSession.KEY).get();
handler.onVoteReceived(ctx.channel(), vote, session.getVersion());
handler.onVoteReceived(vote, session.getVersion(), ctx.channel().remoteAddress().toString());
session.completeVote();
if (session.getVersion() == VotifierSession.ProtocolVersion.ONE) {
@@ -36,7 +44,8 @@ public class VoteInboundHandler extends SimpleChannelInboundHandler<Vote> {
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
VotifierSession session = ctx.channel().attr(VotifierSession.KEY).get();
handler.onError(ctx.channel(), session.hasCompletedVote(), cause);
String remoteAddr = ctx.channel().remoteAddress().toString();
boolean hasCompletedVote = session.hasCompletedVote();
if (session.getVersion() == VotifierSession.ProtocolVersion.TWO) {
JsonObject object = new JsonObject();
@@ -47,5 +56,22 @@ public class VoteInboundHandler extends SimpleChannelInboundHandler<Vote> {
} else {
ctx.close();
}
if (!willThrottleErrorLogging()) {
handler.onError(cause, hasCompletedVote, remoteAddr);
}
}
private boolean willThrottleErrorLogging() {
long lastErrorAt = this.lastError.get();
long now = System.currentTimeMillis();
if (lastErrorAt + 2000 >= now) {
return this.errorsSent.incrementAndGet() >= 5;
} else {
this.lastError.set(now);
this.errorsSent.set(0);
return false;
}
}
}
@@ -3,6 +3,7 @@ package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.net.VotifierSession;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
@@ -11,7 +12,12 @@ import java.nio.charset.StandardCharsets;
/**
* Handles the Votifier greeting.
*/
@ChannelHandler.Sharable
public class VotifierGreetingHandler extends ChannelInboundHandlerAdapter {
public static final VotifierGreetingHandler INSTANCE = new VotifierGreetingHandler();
private VotifierGreetingHandler() {}
@Override
public void channelActive(ChannelHandlerContext ctx) {
VotifierSession session = ctx.channel().attr(VotifierSession.KEY).get();
@@ -3,7 +3,9 @@ package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSA;
import com.vexsoftware.votifier.util.QuietException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CorruptedFrameException;
@@ -16,33 +18,46 @@ import java.util.List;
*/
public class VotifierProtocol1Decoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) {
if (buf.readableBytes() < 256) {
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
if (!ctx.channel().isActive()) {
buf.skipBytes(buf.readableBytes());
return;
}
byte[] block = new byte[buf.readableBytes()];
buf.getBytes(0, block);
// "Drain" the whole buffer
buf.readerIndex(buf.readableBytes());
if (buf.readableBytes() < 256) {
// The client might have not sent all the data yet, so don't eject the connection.
return;
}
if (buf.readableBytes() > 256) {
// They sent too much data.
throw new QuietException("Could not decrypt data from " + ctx.channel().remoteAddress() + " as it is too long. Attack?");
}
byte[] block = ByteBufUtil.getBytes(buf);
buf.skipBytes(buf.readableBytes());
VotifierPlugin plugin = ctx.channel().attr(VotifierPlugin.KEY).get();
try {
block = RSA.decrypt(block, plugin.getProtocolV1Key().getPrivate());
} catch (Exception e) {
throw new CorruptedFrameException("Could not decrypt data (is your key correct?)", e);
if (plugin.isDebug()) {
throw new CorruptedFrameException("Could not decrypt data from " + ctx.channel().remoteAddress() + ". Make sure the public key on the list is correct.", e);
} else {
throw new QuietException("Could not decrypt data from " + ctx.channel().remoteAddress() + ". Make sure the public key on the list is correct.");
}
}
// Parse the string we received.
String all = new String(block, StandardCharsets.US_ASCII);
String[] split = all.split("\n");
if (split.length < 5) {
throw new CorruptedFrameException("Not enough fields specified in vote.");
throw new QuietException("Not enough fields specified in vote. This is not a NuVotifier issue. Got " + split.length + " fields, but needed 5.");
}
if (!split[0].equals("VOTE")) {
throw new CorruptedFrameException("VOTE opcode not found");
throw new QuietException("The VOTE opcode was not present. This is not a NuVotifier issue, but a bug with the server list.");
}
// Create the vote.
@@ -1,10 +1,10 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.util.QuietException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
@@ -16,6 +16,8 @@ import java.util.List;
* Attempts to determine if original protocol or protocol v2 is being used.
*/
public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
private static final QuietException V2_ONLY = new QuietException("This server only accepts well-formed Votifier v2 packets.");
private static final short PROTOCOL_2_MAGIC = 0x733A;
private final boolean testMode;
private final boolean allowv1;
@@ -26,7 +28,7 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) {
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
// Determine the number of bytes that are available.
int readable = buf.readableBytes();
@@ -35,11 +37,7 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
return;
}
short readMagic = buf.readShort();
// Reset reader index again
buf.readerIndex(0);
short readMagic = buf.getShort(0);
VotifierSession session = ctx.channel().attr(VotifierSession.KEY).get();
if (readMagic == PROTOCOL_2_MAGIC) {
@@ -55,7 +53,7 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
}
} else {
if (!allowv1) {
throw new CorruptedFrameException("This server only accepts well-formed Votifier v2 packets.");
throw V2_ONLY;
}
// Probably Protocol v1 Vote Message
session.setVersion(VotifierSession.ProtocolVersion.ONE);
@@ -63,7 +63,7 @@ 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);
String strContents = new String(contents, StandardCharsets.US_ASCII);
strContents = strContents.trim();
try {
return Base64.getDecoder().decode(strContents);
@@ -1,7 +1,5 @@
package com.vexsoftware.votifier.platform;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -1,7 +1,6 @@
package com.vexsoftware.votifier.platform.scheduler;
import com.google.common.base.Preconditions;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -10,7 +9,7 @@ public class ScheduledExecutorServiceVotifierScheduler implements VotifierSchedu
private final ScheduledExecutorService service;
public ScheduledExecutorServiceVotifierScheduler(ScheduledExecutorService service) {
this.service = Preconditions.checkNotNull(service, "service");
this.service = Objects.requireNonNull(service, "service");
}
@Override
@@ -39,7 +39,11 @@ public abstract class AbstractPluginMessagingForwardingSource implements Forward
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());
if (!forwardSpecific(server, rawData)) {
attemptToAddToCache(v, server.getName());
} else if (plugin.isDebug()) {
plugin.getPluginLogger().info("Successfully forwarded vote " + v + " to server " + server.getName());
}
}
}
@@ -11,7 +11,7 @@ public class ServerFilter {
}
public ServerFilter() {
this(Collections.EMPTY_SET, true);
this(Collections.emptySet(), true);
}
private final Collection<String> names;
@@ -1,10 +1,8 @@
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;
@@ -12,6 +10,8 @@ import com.vexsoftware.votifier.util.GsonInst;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.util.*;
import java.util.concurrent.TimeUnit;
@@ -21,8 +21,8 @@ public class FileVoteCache extends MemoryVoteCache {
private final File cacheFile;
private final ScheduledVotifierTask saveTask;
public FileVoteCache(int initialMemorySize, final VotifierPlugin plugin, File cacheFile, long voteTTL) throws IOException {
super(initialMemorySize, plugin, voteTTL);
public FileVoteCache(final VotifierPlugin plugin, File cacheFile, long voteTTL) throws IOException {
super(plugin, voteTTL);
this.cacheFile = cacheFile;
this.l = plugin.getPluginLogger();
@@ -48,9 +48,13 @@ public class FileVoteCache extends MemoryVoteCache {
private void load() throws IOException {
// Load the cache from disk
JsonObject object;
try (BufferedReader reader = Files.newReader(cacheFile, StandardCharsets.UTF_8)) {
try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath(), StandardCharsets.UTF_8)) {
object = GsonInst.gson.fromJson(reader, JsonObject.class);
} catch (FileNotFoundException e) {
if (object == null) {
// When the input is not malformed but instead empty, this returns null. Simply assume it is empty.
object = new JsonObject();
}
} catch (NoSuchFileException e) {
object = new JsonObject();
}
@@ -73,11 +77,11 @@ public class FileVoteCache extends MemoryVoteCache {
JsonObject servers = object.getAsJsonObject("servers");
for (String player : keySet(players)) {
playerVoteCache.putAll(player, readVotes(players.getAsJsonArray(player)));
playerVoteCache.put(player, readVotes(players.getAsJsonArray(player)));
}
for (String server : keySet(servers)) {
voteCache.putAll(server, readVotes(servers.getAsJsonArray(server)));
voteCache.put(server, readVotes(servers.getAsJsonArray(server)));
}
if (resave) {
@@ -98,11 +102,11 @@ public class FileVoteCache extends MemoryVoteCache {
}
}
private Collection<Vote> readVotes(JsonArray voteArray) {
List<Vote> votes = new ArrayList<>(voteArray.size());
private Collection<VoteWithRecordedTimestamp> readVotes(JsonArray voteArray) {
Collection<VoteWithRecordedTimestamp> votes = new HashSet<>(voteArray.size());
for (int i = 0; i < voteArray.size(); i++) {
JsonObject voteObject = voteArray.get(i).getAsJsonObject();
Vote v = new Vote(voteObject);
VoteWithRecordedTimestamp v = new VoteWithRecordedTimestamp(voteObject);
if (hasTimedOut(v))
l.warn("Purging out of date vote.", v);
else
@@ -112,31 +116,32 @@ public class FileVoteCache extends MemoryVoteCache {
}
public void save() throws IOException {
cacheLock.lock();
JsonObject votesObject = new JsonObject();
votesObject.addProperty("version", 2);
cacheLock.lock();
try {
votesObject.add("players", serializeMap(playerVoteCache.asMap()));
votesObject.add("servers", serializeMap(voteCache.asMap()));
votesObject.addProperty("version", 2);
votesObject.add("players", serializeMap(playerVoteCache));
votesObject.add("servers", serializeMap(voteCache));
} finally {
cacheLock.unlock();
}
try (BufferedWriter writer = Files.newWriter(cacheFile, StandardCharsets.UTF_8)) {
try (BufferedWriter writer = Files.newBufferedWriter(cacheFile.toPath(), StandardCharsets.UTF_8)) {
GsonInst.gson.toJson(votesObject, writer);
}
}
public JsonObject serializeMap(Map<String, Collection<Vote>> map) {
public JsonObject serializeMap(Map<String, Collection<VoteWithRecordedTimestamp>> map) {
JsonObject o = new JsonObject();
Iterator<Map.Entry<String, Collection<Vote>>> entryItr = map.entrySet().iterator();
Iterator<Map.Entry<String, Collection<VoteWithRecordedTimestamp>>> entryItr = map.entrySet().iterator();
while (entryItr.hasNext()) {
Map.Entry<String, Collection<Vote>> entry = entryItr.next();
Map.Entry<String, Collection<VoteWithRecordedTimestamp>> entry = entryItr.next();
JsonArray array = new JsonArray();
Iterator<Vote> voteItr = entry.getValue().iterator();
Iterator<VoteWithRecordedTimestamp> voteItr = entry.getValue().iterator();
while (voteItr.hasNext()) {
Vote vote = voteItr.next();
VoteWithRecordedTimestamp vote = voteItr.next();
// if the vote is no longer valid, notify and remove
if (hasTimedOut(vote)) {
@@ -1,8 +1,6 @@
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.google.gson.JsonObject;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import com.vexsoftware.votifier.platform.VotifierPlugin;
@@ -20,16 +18,16 @@ 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 Map<String, Collection<VoteWithRecordedTimestamp>> voteCache;
protected final Map<String, Collection<VoteWithRecordedTimestamp>> 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();
public MemoryVoteCache(VotifierPlugin p, long voteTTL) {
voteCache = new HashMap<>();
playerVoteCache = new HashMap<>();
this.voteTTL = voteTTL;
@@ -41,7 +39,7 @@ public class MemoryVoteCache implements VoteCache {
public Collection<String> getCachedServers() {
cacheLock.lock();
try {
return ImmutableList.copyOf(voteCache.keySet());
return Collections.unmodifiableCollection(new ArrayList<>(voteCache.keySet()));
} finally {
cacheLock.unlock();
}
@@ -52,7 +50,7 @@ public class MemoryVoteCache implements VoteCache {
if (server == null) throw new NullPointerException();
cacheLock.lock();
try {
voteCache.put(server, v);
voteCache.computeIfAbsent(server, k -> new HashSet<>()).add(new VoteWithRecordedTimestamp(v));
} finally {
cacheLock.unlock();
}
@@ -63,7 +61,7 @@ public class MemoryVoteCache implements VoteCache {
if (player == null) throw new NullPointerException();
cacheLock.lock();
try {
playerVoteCache.put(player, v);
playerVoteCache.computeIfAbsent(player, k -> new HashSet<>()).add(new VoteWithRecordedTimestamp(v));
} finally {
cacheLock.unlock();
}
@@ -74,7 +72,12 @@ public class MemoryVoteCache implements VoteCache {
if (player == null) throw new NullPointerException();
cacheLock.lock();
try {
return new HashSet<>(playerVoteCache.removeAll(player));
Collection<VoteWithRecordedTimestamp> playerVotes = playerVoteCache.remove(player);
if (playerVotes != null) {
return new HashSet<>(playerVotes);
} else {
return Collections.emptySet();
}
} finally {
cacheLock.unlock();
}
@@ -85,31 +88,88 @@ public class MemoryVoteCache implements VoteCache {
if (server == null) throw new NullPointerException();
cacheLock.lock();
try {
return new HashSet<>(voteCache.removeAll(server));
Collection<VoteWithRecordedTimestamp> serverVotes = voteCache.remove(server);
if (serverVotes != null) {
return new HashSet<>(serverVotes);
} else {
return Collections.emptySet();
}
} finally {
cacheLock.unlock();
}
}
public void sweep() {
sweep(voteCache);
sweep(playerVoteCache);
cacheLock.lock();
try {
sweep(voteCache);
sweep(playerVoteCache);
} finally {
cacheLock.unlock();
}
}
private void sweep(Multimap<?, Vote> m) {
Iterator<Vote> vi = m.values().iterator();
while (vi.hasNext()) {
Vote v = vi.next();
if (hasTimedOut(v)) {
l.warn("Purging out of date vote.", v);
vi.remove();
private void sweep(Map<String, Collection<VoteWithRecordedTimestamp>> m) {
for (Map.Entry<String, Collection<VoteWithRecordedTimestamp>> entry : m.entrySet()) {
Iterator<VoteWithRecordedTimestamp> vi = entry.getValue().iterator();
while (vi.hasNext()) {
VoteWithRecordedTimestamp v = vi.next();
if (hasTimedOut(v)) {
l.warn("Purging out of date vote.", v);
vi.remove();
}
}
}
}
protected boolean hasTimedOut(Vote v) {
protected boolean hasTimedOut(VoteWithRecordedTimestamp v) {
if (voteTTL == -1) return false;
// scale voteTTL to milliseconds
return v.getLocalTimestamp() + voteTTL * 24 * 60 * 60 * 1000 < System.currentTimeMillis();
long daysAsMillis = TimeUnit.DAYS.toMillis(voteTTL);
return v.recorded + daysAsMillis < System.currentTimeMillis();
}
static class VoteWithRecordedTimestamp extends Vote {
private final long recorded;
VoteWithRecordedTimestamp(Vote vote) {
super(vote);
this.recorded = System.currentTimeMillis();
}
VoteWithRecordedTimestamp(JsonObject object) {
super(object);
if (object.has("recorded")) {
this.recorded = object.get("recorded").getAsLong();
} else {
// Assign the current time, in the hope that the stale votes will eventually get purged.
this.recorded = System.currentTimeMillis();
}
}
@Override
public JsonObject serialize() {
JsonObject object = super.serialize();
object.addProperty("recorded", this.recorded);
return object;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
VoteWithRecordedTimestamp that = (VoteWithRecordedTimestamp) o;
return recorded == that.recorded;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (int) (recorded ^ (recorded >>> 32));
return result;
}
}
}
@@ -10,7 +10,7 @@ import com.vexsoftware.votifier.model.Vote;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
@@ -53,9 +53,9 @@ public class ProxyForwardingVoteSource implements ForwardingVoteSource {
private void forwardVote(final BackendServer server, final Vote v, final int tries) {
nettyBootstrap.get()
.handler(new ChannelInitializer<NioSocketChannel>() {
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) {
protected void initChannel(SocketChannel channel) {
channel.pipeline().addLast(new DelimiterBasedFrameDecoder(256, true, Delimiters.lineDelimiter()));
channel.pipeline().addLast(new ReadTimeoutHandler(5, TimeUnit.SECONDS));
channel.pipeline().addLast(STRING_DECODER);
@@ -0,0 +1,62 @@
package com.vexsoftware.votifier.util;
import com.vexsoftware.votifier.model.Vote;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ArgsToVote {
private static final Pattern argumentPattern = Pattern.compile("([a-zA-Z]+)=(\\S*)");
public static Vote parse(String[] arguments) {
return parse(arguments, null);
}
public static Vote parse(String[] arguments, Map<String, String> additionalArgs) {
String serviceName = "TestVote";
String username = null;
String address = "127.0.0.1";
long localTimestamp = System.currentTimeMillis();
String timestamp = Long.toString(localTimestamp, 10);
for (String s : arguments) {
Matcher m = argumentPattern.matcher(s);
if (m.matches()) {
String key = m.group(1).toLowerCase();
String v = m.group(2);
switch (key) {
case "servicename":
serviceName = v;
break;
case "username":
if (v.length() > 16)
throw new IllegalArgumentException("Illegal username - must be less than 16 characters long.");
username = v;
break;
case "address":
address = v;
break;
case "timestamp":
timestamp = v;
break;
default:
if (additionalArgs != null)
additionalArgs.put(key, v);
break;
}
} else {
if (s.length() > 16)
throw new IllegalArgumentException("Illegal username - must be less than 16 characters long.");
username = s; // ezpz
}
}
if (username == null)
throw new IllegalArgumentException("Username not specified!");
return new Vote(serviceName, username, address, timestamp);
}
}
@@ -0,0 +1,17 @@
package com.vexsoftware.votifier.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class IOUtil {
public static byte[] readAllBytes(InputStream from) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
byte[] buf = new byte[8192];
int read;
while ((read = from.read(buf)) != -1) {
out.write(buf, 0, read);
}
return out.toByteArray();
}
}
@@ -0,0 +1,15 @@
package com.vexsoftware.votifier.util;
public class QuietException extends Exception {
public QuietException() {
}
public QuietException(String s) {
super(s);
}
@Override
public synchronized Throwable fillInStackTrace() {
return null;
}
}
@@ -1,14 +1,13 @@
package com.vexsoftware.votifier.util.standalone;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierServerBootstrap;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.platform.JavaUtilLogger;
import com.vexsoftware.votifier.platform.LoggingAdapter;
import com.vexsoftware.votifier.platform.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.platform.scheduler.ScheduledExecutorServiceVotifierScheduler;
import com.vexsoftware.votifier.platform.scheduler.VotifierScheduler;
import io.netty.channel.Channel;
import java.net.InetSocketAddress;
import java.security.Key;
@@ -69,12 +68,12 @@ public class StandaloneVotifierPlugin implements VotifierPlugin {
}
@Override
public void onVoteReceived(Channel channel, Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
public void onVoteReceived(Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) throws Exception {
receiver.onVote(vote);
}
@Override
public void onError(Channel channel, boolean alreadyHandledVote, Throwable throwable) {
public void onError(Throwable throwable, boolean alreadyHandledVote, String remoteAddress) {
receiver.onException(throwable);
}
}
@@ -85,6 +85,11 @@ public class TestVotifierPlugin implements VotifierPlugin {
return null;
}
@Override
public boolean isDebug() {
return true;
}
public void specificKeysOnly() {
keyMap.clear();
keyMap.put("Test", KeyCreator.createKeyFrom("test"));
@@ -96,7 +101,7 @@ public class TestVotifierPlugin implements VotifierPlugin {
}
@Override
public void onVoteReceived(Channel channel, Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
public void onVoteReceived(Vote vote, VotifierSession.ProtocolVersion protocolVersion, String remoteAddress) throws Exception {
}
}
@@ -26,6 +26,6 @@ public class VoteUtil {
builder.append(s).append('\n'); // naive join needed!
}
return RSA.encrypt(builder.toString().getBytes(StandardCharsets.UTF_8), key);
return RSA.encrypt(builder.toString().getBytes(StandardCharsets.US_ASCII), key);
}
}
@@ -4,18 +4,20 @@ 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;
import com.vexsoftware.votifier.util.QuietException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.CorruptedFrameException;
import org.junit.Test;
import io.netty.handler.codec.DecoderException;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
public class VotifierProtocol1DecoderTest {
private static final VotifierSession SESSION = new VotifierSession();
@@ -50,24 +52,21 @@ public class VotifierProtocol1DecoderTest {
byte[] encrypted = RSA.encrypt(bad.getBytes(StandardCharsets.UTF_8), TestVotifierPlugin.getI().getProtocolV1Key().getPublic());
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
try {
channel.writeInbound(encryptedByteBuf);
} finally {
channel.close();
}
assertThrows(DecoderException.class, () -> channel.writeInbound(encryptedByteBuf));
channel.close();
}
@Test(expected = CorruptedFrameException.class)
@Test
public void testFailureDecodeMissingField() throws Exception {
verifyFailure("VOTE\nTest\ntest\ntest"); // missing field
}
@Test(expected = CorruptedFrameException.class)
@Test
public void testFailureDecodeBadOpcode() throws Exception {
verifyFailure("TEST\nTest\ntest\ntest\ntest\n");
}
@Test(expected = CorruptedFrameException.class)
@Test
public void testFailureDecodeBadRsa() throws Exception {
// Decode our bad RSA key
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
@@ -80,10 +79,7 @@ public class VotifierProtocol1DecoderTest {
byte[] encrypted = VoteUtil.encodePOJOv1(new Vote("Test", "test", "test", "test"), badPublicKey);
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
try {
channel.writeInbound(encryptedByteBuf);
} finally {
channel.close();
}
assertThrows(DecoderException.class, ()->channel.writeInbound(encryptedByteBuf));
channel.close();
}
}
@@ -10,14 +10,14 @@ import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.DecoderException;
import org.json.JSONObject;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
public class VotifierProtocol2DecoderTest {
private static final VotifierSession SESSION = new VotifierSession();
@@ -61,7 +61,7 @@ public class VotifierProtocol2DecoderTest {
sendVote(new Vote("Test", "test", "test", "0"), TestVotifierPlugin.getI().getTokens().get("default"), true);
}
@Test(expected = DecoderException.class)
@Test
public void testFailureDecodeBadPacket() {
// Create a well-formed request
EmbeddedChannel channel = createChannel();
@@ -73,14 +73,11 @@ public class VotifierProtocol2DecoderTest {
object.put("payload", GsonInst.gson.toJson(payload));
// We "forget" the signature.
try {
channel.writeInbound(object.toString());
} finally {
channel.close();
}
assertThrows(DecoderException.class, () -> channel.writeInbound(object.toString()));
channel.close();
}
@Test(expected = DecoderException.class)
@Test
public void testFailureDecodeBadVoteField() throws Exception {
// Create a well-formed request
EmbeddedChannel channel = createChannel();
@@ -96,14 +93,11 @@ public class VotifierProtocol2DecoderTest {
object.put("signature",
Base64.getEncoder().encodeToString(mac.doFinal(payloadEncoded.getBytes(StandardCharsets.UTF_8))));
try {
channel.writeInbound(object.toString());
} finally {
channel.close();
}
assertThrows(DecoderException.class, () -> channel.writeInbound(object.toString()));
channel.close();
}
@Test(expected = DecoderException.class)
@Test
public void testFailureDecodeBadChallenge() throws Exception {
// Create a well-formed request
EmbeddedChannel channel = createChannel();
@@ -120,29 +114,24 @@ public class VotifierProtocol2DecoderTest {
object.put("signature",
Base64.getEncoder().encode(mac.doFinal(payloadEncoded.getBytes(StandardCharsets.UTF_8))));
try {
channel.writeInbound(object.toString());
} finally {
channel.close();
}
assertThrows(DecoderException.class, () -> channel.writeInbound(object.toString()));
channel.close();
}
@Test(expected = DecoderException.class)
@Test
public void testFailureDecodeNonExistentKey() throws Exception {
TestVotifierPlugin.getI().specificKeysOnly();
Vote vote = new Vote("Bad Service", "test", "test", "0");
try {
sendVote(vote, TestVotifierPlugin.getI().getTokens().get("Test"), false);
} finally {
TestVotifierPlugin.getI().restoreDefault();
}
assertThrows(DecoderException.class, () -> sendVote(vote, TestVotifierPlugin.getI().getTokens().get("Test"), false));
TestVotifierPlugin.getI().restoreDefault();
}
@Test(expected = CorruptedFrameException.class)
public void testFailureDecodeBadSignature() throws Exception {
@Test
public void testFailureDecodeBadSignature() {
Vote vote = new Vote("Bad Service", "test", "test", "0");
sendVote(vote, KeyCreator.createKeyFrom("BadKey"), false);
assertThrows(CorruptedFrameException.class, () -> sendVote(vote, KeyCreator.createKeyFrom("BadKey"), false));
}
}
@@ -2,13 +2,16 @@ package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.util.QuietException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.DecoderException;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class VotifierProtocolDifferentiatorTest {
@Test
@@ -43,7 +46,7 @@ public class VotifierProtocolDifferentiatorTest {
channel.close();
}
@Test(expected = DecoderException.class)
@Test
public void failIfv1NotSupported() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
@@ -54,13 +57,11 @@ public class VotifierProtocolDifferentiatorTest {
for (int i = 0; i < 256; i++) {
test.writeByte(0);
}
channel.writeInbound(test);
assertEquals(VotifierSession.ProtocolVersion.ONE, session.getVersion());
assertThrows(DecoderException.class, () -> channel.writeInbound(test));
channel.close();
}
@Test(expected = DecoderException.class)
@Test
public void failOnBadPacketTest() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
@@ -69,11 +70,8 @@ public class VotifierProtocolDifferentiatorTest {
buf.writeByte(0);
}
try {
channel.writeInbound(buf);
} finally {
channel.close();
}
assertThrows(DecoderException.class, () -> channel.writeInbound(buf));
channel.close();
}
@Test
@@ -1,26 +1,26 @@
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 org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class PMForwardingSinkTest {
@Test
public void testSuccessfulMultiDecode() throws Exception {
List<Vote> receivedVotes = Lists.newArrayList();
List<Vote> receivedVotes = new ArrayList<>();
ForwardedVoteListener vl = receivedVotes::add;
List<Vote> sentVotes = Lists.newArrayList(
List<Vote> sentVotes = new ArrayList<>(Arrays.asList(
new Vote("serviceA", "usernameA", "1.1.1.1", "1546300800"),
new Vote("serviceB", "usernameBBBBBBB", "1.2.23.4", "1514764800", System.currentTimeMillis())
);
new Vote("serviceB", "usernameBBBBBBB", "1.2.23.4", "1514764800")
));
StringBuilder message = new StringBuilder();
@@ -28,7 +28,7 @@ public class PMForwardingSinkTest {
message.append(v.serialize());
}
byte[] messageBytes = message.toString().getBytes();
byte[] messageBytes = message.toString().getBytes(StandardCharsets.UTF_8);
System.out.println(message.toString());
AbstractPluginMessagingForwardingSink sink = new AbstractPluginMessagingForwardingSink(vl) {
@@ -0,0 +1,108 @@
package com.vexsoftware.votifier.util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.vexsoftware.votifier.model.Vote;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
public class ArgsToVoteTest {
@Test
void handlesInvalidRequest() {
assertThrows(IllegalArgumentException.class, () -> {
ArgsToVote.parse(new String[0]);
});
}
@Test
void handlesSingleUsername() {
Vote vote = ArgsToVote.parse(new String[] { "dummy_user" });
assertEquals("dummy_user", vote.getUsername());
}
@Test
void handlesSingleUsernameTooLong() {
assertThrows(IllegalArgumentException.class, () -> {
ArgsToVote.parse(new String[] { "AVeryVeryVeryLongUsername" });
});
}
@ParameterizedTest
@ValueSource(strings = { "servicename", "address", "timestamp" })
void handlesAdditionalKeywordArguments(String key) throws Exception {
Vote vote = ArgsToVote.parse(new String[] { "username=abc123", key + "=dummy" });
// Find the getter on the Vote object
for (Method method : Vote.class.getDeclaredMethods()) {
if (method.getName().toLowerCase(Locale.ENGLISH).equals("get" + key)) {
assertEquals("dummy", method.invoke(vote));
return;
}
}
fail("Unable to find appropriate getter - this is a bug");
}
@Test
void handlesUsernameKeywordArgument() {
Vote vote = ArgsToVote.parse(new String[] { "username=dummy_user" });
assertEquals("dummy_user", vote.getUsername());
}
@ParameterizedTest
@ValueSource(strings = { "servicename", "address", "timestamp" })
void handlesMixedUsernameAndKeywordArgument(String key) throws Exception {
Vote vote = ArgsToVote.parse(new String[] { "abc123", key + "=dummy" });
assertEquals("abc123", vote.getUsername());
// Find the getter on the Vote object
for (Method method : Vote.class.getDeclaredMethods()) {
if (method.getName().toLowerCase(Locale.ENGLISH).equals("get" + key)) {
assertEquals("dummy", method.invoke(vote));
return;
}
}
fail("Unable to find appropriate getter - this is a bug");
}
@ParameterizedTest
@MethodSource("powerSetArgs")
void handlesMixedKeywordArguments(Set<String> keys) throws Exception {
String[] args = new String[keys.size() + 1];
args[0] = "abc123";
for (int i = 0; i < keys.size(); i++) {
args[i + 1] = ImmutableList.copyOf(keys).get(i) + "=dummy";
}
Vote vote = ArgsToVote.parse(args);
assertEquals("abc123", vote.getUsername());
// Find the getters on the Vote object
outer: for (String key : keys) {
for (Method method : Vote.class.getDeclaredMethods()) {
if (method.getName().toLowerCase(Locale.ENGLISH).equals("get" + key)) {
assertEquals("dummy", method.invoke(vote));
continue outer;
}
}
fail("Unable to find appropriate getter - this is a bug");
}
}
private static Stream<Set<String>> powerSetArgs() {
return Sets.powerSet(ImmutableSet.of("servicename", "address", "timestamp")).stream();
}
}
+16 -6
View File
@@ -1,22 +1,32 @@
.PHONY: clean, all, run
.PHONY: clean all run
all : resources/paper.jar resources/proxy.jar
all : resources/paper.jar resources/waterfall.jar resources/velocity.jar resources/sponge.jar resources/viaversion.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
wget -O resources/paper.jar https://papermc.io/api/v1/paper/1.16.2/182/download
resources/proxy.jar : resources
wget -O resources/proxy.jar https://papermc.io/ci/job/Waterfall/lastSuccessfulBuild/artifact/Waterfall-Proxy/bootstrap/target/Waterfall.jar
resources/waterfall.jar : resources
wget -O resources/waterfall.jar https://papermc.io/ci/job/Waterfall/lastSuccessfulBuild/artifact/Waterfall-Proxy/bootstrap/target/Waterfall.jar
resources/velocity.jar : resources
wget -O resources/velocity.jar https://ci.velocitypowered.com/job/velocity-1.1.0/lastSuccessfulBuild/artifact/proxy/build/libs/velocity-proxy-1.1.0-SNAPSHOT-all.jar
resources/sponge.jar : resources
wget -O resources/sponge.jar https://repo.spongepowered.org/maven/org/spongepowered/spongevanilla/1.12.2-7.3.0/spongevanilla-1.12.2-7.3.0.jar
resources/viaversion.jar : resources
wget -O resources/viaversion.jar https://ci.viaversion.com/job/ViaVersion/459/artifact/jar/target/ViaVersion-3.1.1-SNAPSHOT.jar
clean :
rm -r resources/
run :
tmux new-session './proxy/run.sh' \; \
tmux new-session './p1/run.sh' \; \
split-window -h './p2/run.sh' \; \
split-window -h './s1/run.sh' \; \
split-window -h './s2/run.sh' \; \
select-layout even-horizontal
+10 -7
View File
@@ -1,7 +1,7 @@
## 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.
This testbed runs two proxies, labeled p1 and p2, as well as two servers behind it,
labeled s1 and s2. Ports for where everything is bound is listed below.
First, download required sources using the following command:
@@ -19,8 +19,11 @@ 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 |
| Server | Server Software | MC Port | Votifier Port |
| ------ | ---------------------- | ------- | ------------- |
| p1 | Waterfall/BungeeCord | 25577 | 8192 |
| p2 | Velocity | 25578 | 8193 |
| s1 | Paper (1.16.1) | 25565 | 8194 |
| s2 | SpongeVanilla (1.12.2) | 25566 | 8195 |
For compatibility with 1.16.1, s2 includes ViaVersion.
File renamed without changes.
+18 -14
View File
@@ -1,7 +1,4 @@
forge_support: true
timeout: 30000
online_mode: true
log_commands: false
server_connect_timeout: 5000
listeners:
- query_port: 25577
motd: '&4its a bungee cord surbur'
@@ -17,16 +14,8 @@ listeners:
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
remote_ping_cache: -1
network_compression_threshold: 256
disabled_commands: []
stats: bf8a54ea-2917-4b9e-b463-2c05bd7f1ac6
permissions:
default:
- bungeecord.command.server
@@ -36,6 +25,22 @@ permissions:
- bungeecord.command.end
- bungeecord.command.ip
- bungeecord.command.reload
log_pings: true
connection_throttle_limit: 3
prevent_proxy_connections: true
timeout: 30000
player_limit: -1
ip_forward: true
groups:
md_5:
- admin
remote_ping_timeout: 5000
connection_throttle: 1
log_commands: false
stats: bf8a54ea-2917-4b9e-b463-2c05bd7f1ac6
online_mode: true
forge_support: true
disabled_commands: []
servers:
s1:
motd: SERVER 1
@@ -45,4 +50,3 @@ servers:
motd: SERVER 2
address: localhost:25566
restricted: false
player_limit: -1
File renamed without changes.
+2
View File
@@ -0,0 +1,2 @@
rsa/
cached-votes.json
@@ -7,12 +7,12 @@ 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
debug: true
# All tokens, labeled by the serviceName of each server list.
tokens:
# Default token for all server lists, if another isn't supplied.
default: jqpli3gnd8l67qsg2d81vje09h
default: proxy1token
# 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.
@@ -24,7 +24,7 @@ forwarding:
# - 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
method: pluginMessaging
pluginMessaging:
channel: nuvotifier:votes
@@ -46,11 +46,11 @@ forwarding:
# most set ups.
cache: file
# Only sends the vote to the server in which the player is currently joined.
onlySendToJoinedServer: false
onlySendToJoinedServer: true
#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'
joinedServerFallback: 's1'
# 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.
@@ -66,9 +66,9 @@ forwarding:
proxy:
Hub:
address: 127.0.0.1
port: 8193
token: MyKeyHere
port: 8194
token: server1token
Hub2:
address: 127.0.0.1
port: 8194
token: AnotherKeyHere
port: 8195
token: server2token
File renamed without changes.
+1
View File
@@ -0,0 +1 @@
../resources/waterfall.jar
View File
File renamed without changes.
@@ -1,8 +1,9 @@
use_netty_dns_resolver: true
disable_modern_tab_limiter: true
log_initial_handler_connections: true
allow_empty_packets: false
throttling:
tab_complete: 1000
game_version: ''
disable_modern_tab_limiter: true
use_netty_dns_resolver: true
disable_entity_metadata_rewrite: false
log_initial_handler_connections: true
log_server_list_ping: false
allow_empty_packets: false
+1
View File
@@ -0,0 +1 @@
logs/
File renamed without changes.
+2
View File
@@ -0,0 +1,2 @@
rsa/
cached-votes.json
+79
View File
@@ -0,0 +1,79 @@
# 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
# 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 = "proxy2token"
# 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 = "pluginMessaging"
[forwarding.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 = "s1"
# Defines how quickly to dump votes over a player's connection while offloading a cache in votes per second
dumpRate = 5
[forwarding.file-cache]
# Options for file caching.
name = "cached-votes.json"
# days before a vote is considered 'dead' - removed from cache with a console warning. -1 signifies no cache.
cacheTime = 10
[forwarding.memory-cache]
# Options for file caching.
name = "cached-votes.json"
# days before a vote is considered 'dead' - removed from cache with a console warning. -1 signifies no cache. Votes are
# also removed on restart
cacheTime = 10
[forwarding.proxy]
# Specify servers to proxy votes for.
[forwarding.proxy.Hub]
address = "127.0.0.1"
port = 8194
token = "server1token"
[forwarding.proxy.Hub2]
address = "127.0.0.1"
port = 8195
token = "server2token"
+1
View File
@@ -0,0 +1 @@
../resources/velocity.jar
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd $DIR
java -jar proxy.jar
+135
View File
@@ -0,0 +1,135 @@
# Config version. Do not change this
config-version = "1.0"
# What port should the proxy be bound to? By default, we'll bind to all addresses on port 25577.
bind = "0.0.0.0:25578"
# What should be the MOTD? This gets displayed when the player adds your server to
# their server list. Legacy color codes and JSON are accepted.
motd = "&3doot doot imma velocity serbur"
# What should we display for the maximum number of players? (Velocity does not support a cap
# on the number of players online.)
show-max-players = 500
# Should we authenticate players with Mojang? By default, this is on.
online-mode = true
# If client's ISP/AS sent from this proxy is different from the one from Mojang's
# authentication server, the player is kicked. This disallows some VPN and proxy
# connections but is a weak form of protection.
prevent-client-proxy-connections = false
# Should we forward IP addresses and other data to backend servers?
# Available options:
# - "none": No forwarding will be done. All players will appear to be connecting
# from the proxy and will have offline-mode UUIDs.
# - "legacy": Forward player IPs and UUIDs in a BungeeCord-compatible format. Use this
# if you run servers using Minecraft 1.12 or lower.
# - "bungeeguard": Forward player IPs and UUIDs in a format supported by the BungeeGuard
# plugin. Use this if you run servers using Minecraft 1.12 or lower, and are
# unable to implement network level firewalling (on a shared host).
# - "modern": Forward player IPs and UUIDs as part of the login process using
# Velocity's native forwarding. Only applicable for Minecraft 1.13 or higher.
player-info-forwarding-mode = "LEGACY"
# If you are using modern or BungeeGuard IP forwarding, configure an unique secret here.
forwarding-secret = "not at all secret lolololol"
# Announce whether or not your server supports Forge. If you run a modded server, we
# suggest turning this on.
#
# If your network runs one modpack consistently, consider using ping-passthrough = "mods"
# instead for a nicer display in the server list.
announce-forge = false
# If enabled (default is false) and the proxy is in online mode, Velocity will kick
# any existing player who is online if a duplicate connection attempt is made.
kick-existing-players = false
# Should Velocity pass server list ping requests to a backend server?
# Available options:
# - "disabled": No pass-through will be done. The velocity.toml and server-icon.png
# will determine the initial server list ping response.
# - "mods": Passes only the mod list from your backend server into the response.
# The first server in your try list (or forced host) with a mod list will be
# used. If no backend servers can be contacted, Velocity won't display any
# mod information.
# - "description": Uses the description and mod list from the backend server. The first
# server in the try (or forced host) list that responds is used for the
# description and mod list.
# - "all": Uses the backend server's response as the proxy response. The Velocity
# configuration is used if no servers could be contacted.
ping-passthrough = "DISABLED"
[servers]
# Configure your servers here. Each key represents the server's name, and the value
# represents the IP address of the server to connect to.
s1 = "127.0.0.1:25565"
s2 = "127.0.0.1:25566"
# In what order we should try servers when a player logs in or is kicked from aserver.
try = [
"s1"
]
[forced-hosts]
# Configure your forced hosts here.
[advanced]
# How large a Minecraft packet has to be before we compress it. Setting this to zero will
# compress all packets, and setting it to -1 will disable compression entirely.
compression-threshold = 256
# How much compression should be done (from 0-9). The default is -1, which uses the
# default level of 6.
compression-level = -1
# How fast (in milliseconds) are clients allowed to connect after the last connection? By
# default, this is three seconds. Disable this by setting this to 0.
login-ratelimit = 3000
# Specify a custom timeout for connection timeouts here. The default is five seconds.
connection-timeout = 5000
# Specify a read timeout for connections here. The default is 30 seconds.
read-timeout = 30000
# Enables compatibility with HAProxy.
proxy-protocol = false
# Enables TCP fast open support on the proxy. Requires the proxy to run on Linux.
tcp-fast-open = false
# Enables BungeeCord plugin messaging channel support on Velocity.
bungee-plugin-message-channel = true
# Shows ping requests to the proxy from clients.
show-ping-requests = false
[query]
# Whether to enable responding to GameSpy 4 query responses or not.
enabled = false
# If query is enabled, on what port should the query protocol listen on?
port = 25577
# This is the map name that is reported to the query services.
map = "Velocity"
# Whether plugins should be shown in query response by default or not
show-plugins = false
[metrics]
# Whether metrics will be reported to bStats (https://bstats.org).
# bStats collects some basic information, like how many people use Velocity and their
# player count. We recommend keeping bStats enabled, but if you're not comfortable with
# this, you can turn this setting off. There is no performance penalty associated with
# having metrics enabled, and data sent to bStats can't identify your server.
enabled = true
# A unique, anonymous ID to identify this proxy with.
id = "729fe923-b383-4e5b-b456-5adf554bfe69"
log-failure = false
-1
View File
@@ -1 +0,0 @@
rsa/
-1
View File
@@ -1 +0,0 @@
../resources/proxy.jar
+1 -1
View File
@@ -7,4 +7,4 @@ 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
java -DIReallyKnowWhatIAmDoingISwear -Dcom.mojang.eula.agree=true -jar paper.jar nogui
+5
View File
@@ -13,6 +13,7 @@
settings:
minimum-api: none
allow-end: true
warn-on-overload: true
permissions-file: permissions.yml
@@ -23,6 +24,7 @@ settings:
deprecated-verbose: default
shutdown-message: Server closed
spawn-limits:
water-ambient: 20
monsters: 70
animals: 10
water-animals: 15
@@ -31,6 +33,9 @@ chunk-gc:
period-in-ticks: 600
load-threshold: 0
ticks-per:
water-ambient-spawns: 1
water-spawns: 1
ambient-spawns: 1
animal-spawns: 400
monster-spawns: 1
autosave: 6000
+52 -6
View File
@@ -11,8 +11,11 @@
# Docs: https://paper.readthedocs.org/
verbose: false
config-version: 16
config-version: 20
settings:
max-joins-per-tick: 3
console-has-all-permissions: false
chunk-tasks-per-tick: 1000
load-permissions-yml-before-plugins: true
bungee-online-mode: true
use-versioned-world: false
@@ -41,10 +44,20 @@ settings:
max-player-auto-save-per-tick: -1
async-chunks:
enable: true
generation: true
thread-per-world-generation: true
load-threads: -1
threads: -1
use-alternative-luck-formula: false
unsupported-settings:
allow-headless-pistons: false
allow-permanent-block-break-exploits: false
allow-piston-duplication: false
allow-permanent-block-break-exploits-readme: This setting controls if players
should be able to break bedrock, end portals and other intended to be permanent
blocks.
allow-piston-duplication-readme: This setting controls if player should be able
to use TNT duplication, but this also allows duplicating carpet, rails and potentially
other items
allow-headless-pistons-readme: This setting controls if players should be able
to create headless pistons.
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.'
@@ -54,6 +67,7 @@ messages:
flying-vehicle: Flying is not enabled on this server
connection-throttle: Connection throttled! Please wait before reconnecting.
timings:
server-name: Unknown Server
enabled: true
verbose: true
server-name-privacy: false
@@ -64,8 +78,24 @@ timings:
history-length: 3600
world-settings:
default:
zombies-target-turtle-eggs: true
per-player-mob-spawns: false
phantoms-do-not-spawn-on-creative-players: true
phantoms-only-attack-insomniacs: true
light-queue-size: 20
count-all-mobs-for-spawning: false
seed-based-feature-search: true
nether-ceiling-void-damage-height: 0
fix-zero-tick-instant-grow-farms: true
portal-create-radius: 16
iron-golems-can-spawn-in-air: false
entities-target-with-follow-range: false
zombie-villager-infection-chance: -1.0
portal-search-radius: 128
game-mechanics:
disable-pillager-patrols: false
nerf-pigmen-from-nether-portals: false
disable-relative-projectile-velocity: false
villages-load-chunks: false
scan-for-legacy-ender-dragon: true
disable-chest-cat-detection: false
@@ -74,6 +104,14 @@ world-settings:
disable-sprint-interruption-on-attack: false
shield-blocking-delay: 5
disable-unloaded-chunk-enderpearl-exploit: true
pillager-patrols:
spawn-chance: 0.2
spawn-delay:
per-player: false
ticks: 12000
start:
per-player: false
day: 5
queue-light-updates: false
optimize-explosions: false
disable-teleportation-suffocation-check: false
@@ -141,13 +179,12 @@ world-settings:
- chest
- diamond_ore
- redstone_ore
- lit_redstone_ore
- clay
- emerald_ore
- ender_chest
replacement-blocks:
- stone
- planks
- oak_planks
skip-entity-ticking-in-chunks-scheduled-for-unload: true
armor-stands-do-collision-entity-lookups: true
keep-spawn-loaded-range: 8
@@ -176,3 +213,12 @@ world-settings:
mob-spawner-tick-rate: 1
squid-spawn-height:
maximum: 0.0
alt-item-despawn-rate:
enabled: false
items:
COBBLESTONE: 300
viewdistances:
no-tick-view-distance: -1
generator-settings:
flat-bedrock: false
baby-zombie-movement-modifier: 0.5
+1 -1
View File
@@ -3,7 +3,7 @@
host: 0.0.0.0
# Port to listen for new votes on
port: 8193
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.
+1
View File
@@ -0,0 +1 @@
../../../universal/build/libs/nuvotifier.jar
+19 -6
View File
@@ -1,26 +1,36 @@
#Minecraft server properties
#Fri Dec 21 15:15:00 EST 2018
#Thu Sep 03 15:49:51 EDT 2020
spawn-protection=16
max-tick-time=60000
query.port=25565
generator-settings=
sync-chunk-writes=true
force-gamemode=false
allow-nether=true
enforce-whitelist=false
gamemode=0
gamemode=survival
broadcast-console-to-ops=true
enable-query=false
player-idle-timeout=0
difficulty=1
difficulty=easy
spawn-monsters=true
broadcast-rcon-to-ops=true
op-permission-level=4
pvp=true
entity-broadcast-range-percentage=100
snooper-enabled=true
level-type=DEFAULT
level-type=default
hardcore=false
enable-status=true
enable-command-block=false
max-players=20
network-compression-threshold=256
resource-pack-sha1=
max-world-size=29999984
function-permission-level=2
rcon.port=25575
server-port=25565
debug=false
server-ip=
spawn-npcs=true
allow-flight=false
@@ -29,11 +39,14 @@ view-distance=10
resource-pack=
spawn-animals=true
white-list=false
rcon.password=
generate-structures=true
online-mode=false
max-build-height=256
online-mode=false
level-seed=
prevent-proxy-connections=false
use-native-transport=true
motd=A Minecraft Server
enable-jmx-monitoring=false
rate-limit=0
enable-rcon=false
motd=A Minecraft Server
+37 -2
View File
@@ -10,8 +10,9 @@
# IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )
# Forums: http://www.spigotmc.org/
config-version: 11
config-version: 12
settings:
log-villager-deaths: true
debug: false
save-user-cache-on-stop-only: false
sample-count: 12
@@ -60,6 +61,16 @@ advancements:
- minecraft:story/disabled
world-settings:
default:
seed-bastion: 30084232
seed-fortress: 30084232
seed-mansion: 10387319
seed-fossil: 14357921
seed-portal: 34222645
seed-outpost: 165745296
seed-endcity: 10387313
view-distance: default
trident-despawn-rate: 1200
end-portal-sound-radius: 0
verbose: true
merge-radius:
exp: 3.0
@@ -91,6 +102,12 @@ world-settings:
min: 45.0
mob-spawn-range: 8
growth:
beetroot-modifier: 100
carrot-modifier: 100
potato-modifier: 100
bamboo-modifier: 100
sweetberry-modifier: 100
kelp-modifier: 100
cactus-modifier: 100
cane-modifier: 100
melon-modifier: 100
@@ -102,11 +119,30 @@ world-settings:
vine-modifier: 100
cocoa-modifier: 100
entity-activation-range:
raiders: 48
villagers: 32
flying-monsters: 32
villagers-work-immunity-after: 100
villagers-work-immunity-for: 20
villagers-active-for-panic: true
animals: 32
monsters: 32
misc: 16
water: 16
tick-inactive-villagers: true
wake-up-inactive:
animals-max-per-tick: 4
animals-every: 1200
animals-for: 100
monsters-max-per-tick: 8
monsters-every: 400
monsters-for: 100
villagers-max-per-tick: 4
villagers-every: 600
villagers-for: 100
flying-monsters-max-per-tick: 8
flying-monsters-every: 200
flying-monsters-for: 100
entity-tracking-range:
players: 48
animals: 48
@@ -120,7 +156,6 @@ world-settings:
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
+2 -4
View File
@@ -3,17 +3,15 @@ banned-players.json
cache/
eula.txt
logs/
permissions.yml
ops.json
usercache.json
usernamecache.json
version_history.json
whitelist.json
world/
world_nether/
world_the_end/
.mixin.out
-37
View File
@@ -1,37 +0,0 @@
# This is the main configuration file for Bukkit.
# As you can see, there's actually not that much to configure without any plugins.
# For a reference for any variable inside this file, check out the Bukkit Wiki at
# https://www.spigotmc.org/go/bukkit-yml
#
# If you need help on this file, feel free to join us on irc or leave a message
# on the forums asking for advice.
#
# IRC: #spigot @ irc.spi.gt
# (If this means nothing to you, just go to https://www.spigotmc.org/go/irc )
# Forums: https://www.spigotmc.org/
# Bug tracker: https://www.spigotmc.org/go/bugs
settings:
allow-end: true
warn-on-overload: true
permissions-file: permissions.yml
update-folder: update
plugin-profiling: false
connection-throttle: 4000
query-plugins: true
deprecated-verbose: default
shutdown-message: Server closed
spawn-limits:
monsters: 70
animals: 10
water-animals: 15
ambient: 15
chunk-gc:
period-in-ticks: 600
load-threshold: 0
ticks-per:
animal-spawns: 400
monster-spawns: 1
autosave: 6000
aliases: now-in-commands.yml
-17
View File
@@ -1,17 +0,0 @@
# This is the commands configuration file for Bukkit.
# For documentation on how to make use of this file, check out the Bukkit Wiki at
# https://www.spigotmc.org/go/commands-yml
#
# If you need help on this file, feel free to join us on irc or leave a message
# on the forums asking for advice.
#
# IRC: #spigot @ irc.spi.gt
# (If this means nothing to you, just go to https://www.spigotmc.org/go/irc )
# Forums: https://www.spigotmc.org/
# Bug tracker: https://www.spigotmc.org/go/bugs
command-block-overrides: []
ignore-vanilla-permissions: false
aliases:
icanhasbukkit:
- version $1-
+1
View File
@@ -0,0 +1 @@
rsa/
+8
View File
@@ -0,0 +1,8 @@
host: 127.0.0.1
port: 8195
debug: true
disable-v1-protocol: false
tokens: {default: server2token}
forwarding:
method: pluginMessaging
pluginMessaging: {channel: 'nuvotifier:votes'}
+43
View File
@@ -0,0 +1,43 @@
# 1.0
#
# # If you need help with the configuration or have any questions related to Sponge,
# # join us at the IRC or drop by our forums and leave a post.
#
# # IRC: #sponge @ irc.esper.net ( https://webchat.esper.net/?channel=sponge )
# # Forums: https://forums.spongepowered.org/
#
sponge {
data-registration {
# A configurable list of registration ids that are to be removed
# when discovered for deserialization. This can be controlled by
# commands in sponge. It is adviseable to refer to the lists made
# available through 'failed-data-list', as using any id's from
# 'registered-data' will result in custom data being deleted at
# every load.
data-to-purge=[]
# An auto generated list, by Sponge, to discover and list
# all failed custom data deserializations at runtime due
# to a lack of the registrations being made by a plugin.
# Not to be confused by failed deserialization due to bad data.
# Modifying the list will result in no effect as Sponge auto
# generates this list. This is merely for user configuration.
failed-data-list=[]
# In the cases where there is already previously discovered data
# we don't want to spam the log on each discovery in certain
# contexts. If it is required, we still can emit the log warning
# when necessary.
print-on-discovery=false
# An auto generated list, by Sponge, to provide a list of
# registered custom data manipulators by plugins. Since
# the list is generated AFTER the game starts, modifying
# this list will not affect Sponge's system in any way.
# However, it is advisable to view what registered datas
# exist on a server instance, such that when Sponge completes
# startup, it will be verified that all existing registrations
# are accounted for. A warning will be emitted for any existing
# registrations that were not registered, and moved to the
# 'failed-data-list'.
registered-data=[]
}
}
+697
View File
@@ -0,0 +1,697 @@
# 1.0
#
# # If you need help with the configuration or have any questions related to Sponge,
# # join us at the IRC or drop by our forums and leave a post.
#
# # IRC: #sponge @ irc.esper.net ( https://webchat.esper.net/?channel=sponge )
# # Forums: https://forums.spongepowered.org/
#
sponge {
# Stopgap measures for dealing with broken mods
broken-mods {
# A list of mod ids that have broken network handlers (they interact with the game from a Netty handler thread).
# All network handlers from a forcibly scheduled to run on the main thread.
# Note that this setting should be considered a last resort, and should only be used as a stopgap measure
# while waiting for a mod to properly fix the issue.
broken-network-handler-mods=[]
}
# Controls how Sponge interacts with server proxies, such as BungeeCord and Velocity.
# Requires that the 'bungeecord' module is enabled.
bungeecord {
# If 'true', allows BungeeCord to forward IP address, UUID, and Game Profile to this server.
ip-forwarding=true
}
# Configuration options related to Sponge's cause tracking system
cause-tracker {
# A mapping that is semi-auto-populating for TileEntities whose types
# are found to be providing 'null' Block sources as neighbor notifications
# that end up causing crashes or spam reports. If the value is set to
# 'true', then a "workaround" will be attempted. If not, the
# current BlockState at the target source will be queried from the world.
# This map having a specific
# entry of a TileEntity will prevent a log or warning come up to any logs
# when that 'null' arises, and Sponge will self-rectify the TileEntity
# by calling the method "getBlockType()". It is advised that if the mod
# id in question is coming up, that the mod author is notified about the
# error-prone usage of the field "blockType". You can refer them to
# the following links for the issue:
# https://gist.github.com/gabizou/ad570dc09dfed259cac9d74284e78e8b
# https://github.com/SpongePowered/SpongeForge/issues/2787
# Also, please provide them with these links for the example PR to
# fix the issue itself, as the fix is very simple:
# https://github.com/TehNut/Soul-Shards-Respawn/pull/24
# https://github.com/Epoxide-Software/Enchanting-Plus/pull/135
#
auto-fix-null-source-block-providing-tile-entities {}
# If set to 'true', when a mod or plugin attempts to spawn an entity
# off the main server thread, Sponge will automatically
# capture said entity to spawn it properly on the main
# server thread. The catch to this is that some mods are
# not considering the consequences of spawning an entity
# off the server thread, and are unaware of potential race
# conditions they may cause. If this is set to false,
# Sponge will politely ignore the entity being spawned,
# and emit a warning about said spawn anyways.
capture-async-spawning-entities=true
# If 'true', more thorough debugging for PhaseStates
# such that a StackTrace is created every time a PhaseState
# switches, allowing for more fine grained troubleshooting
# in the cases of runaway phase states. Note that this is
# not extremely performant and may have some associated costs
# with generating the stack traces constantly.
generate-stacktrace-per-phase=false
# The maximum number of times to recursively process transactions in a single phase.
# Some mods may interact badly with Sponge's block capturing system, causing Sponge to
# end up capturing block transactions every time it tries to process an existing batch.
# Due to the recursive nature of the depth-first processing that Sponge uses to handle block transactions,
# this can result in a stack overflow, which causes us to lose all infomration about the original cause of the issue.
# To prevent a stack overflow, Sponge tracks the current processing depth, and aborts processing when it exceeds
# this threshold.
# The default value should almost always work properly - it's unlikely you'll ever have to change it.
max-block-processing-depth=1000
# If verbose is not enabled, this restricts the amount of
# runaway phase state printouts, usually happens on a server
# where a PhaseState is not completing. Although rare, it should
# never happen, but when it does, sometimes it can continuously print
# more and more. This attempts to placate that while a fix can be worked on
# to resolve the runaway. If 'verbose' is enabled, they will always print.
maximum-printed-runaway-counts=3
# If true, when a mod attempts to perform a neighbor notification
# on a block, some mods do not know to perform a 'null' check
# on the source block of their TileEntity. This usually goes by
# unnoticed by other mods, because they may perform '==' instance
# equality checks instead of calling methods on the potentially
# null Block, but Sponge uses the block to build information to
# help tracking. This has caused issues in the past. Generally,
# this can be useful for leaving 'true' so a proper report is
# generated once for your server, and can be reported to the
# offending mod author.
# This is 'false' by default in SpongeVanilla.
# Review the following links for more info:
# https://gist.github.com/gabizou/ad570dc09dfed259cac9d74284e78e8b
# https://github.com/SpongePowered/SpongeForge/issues/2787
#
report-null-source-blocks-on-neighbor-notifications=true
# If set to 'true', when a mod or plugin attempts to submit a command
# asynchronously, Sponge will automatically capture said command
# and submit it for processing on the server thread. The catch to
# this is that some mods are performing these commands in vanilla
# without considering the possible consequences of such commands
# affecting any thread-unsafe parts of Minecraft, such as worlds,
# block edits, entity spawns, etc. If this is set to false, Sponge
# will politely ignore the command being executed, and emit a warning
# about said command anyways.
resync-commands-from-async=true
# If 'true', the phase tracker will print out when there are too many phases
# being entered, usually considered as an issue of phase re-entrance and
# indicates an unexpected issue of tracking phases not to complete.
# If this is not reported yet, please report to Sponge. If it has been
# reported, you may disable this.
verbose=true
# If 'true', the phase tracker will dump extra information about the current phases
# when certain non-PhaseTracker related exceptions occur. This is usually not necessary, as the information
# in the exception itself can normally be used to determine the cause of the issue.
verbose-errors=false
}
# Configuration options related to commands, including command aliases and hidden commands.
commands {
# Command aliases will resolve conflicts when multiple plugins request a specific command.
# Correct syntax is '<unqualified command>=<plugin name>' e.g. 'sethome=homeplugin'
aliases {}
# Defines how Sponge should act when a user tries to access a command they do not have
# permission for
command-hiding {
# If this is true, when a user tries to tab complete a command, or use "/sponge which" or
# "/sponge:help" this prevents commands a user does not have permission for from being completed.
#
# Note that some commands may not show up during tab complete if a user does not have permission
# regardless of this setting.
hide-on-discovery-attempt=true
# If this is true, when a user tries to use a command they don't have permission for, Sponge
# will act as if the command doesn't exist, rather than showing a no permissions message.
hide-on-execution-attempt=false
}
# Some mods may not trigger a permission check when running their command. Setting this to
# 'true' will enforce a check of the Sponge provided permission ('<modid>.command.<commandname>').
# Note that setting this to 'true' may cause some commands that are generally accessible to all to
# require a permission to run.
#
# Setting this to 'true' will enable greater control over whether a command will appear in
# tab completion and Sponge's help command.
#
# If you are not using a permissions plugin, it is highly recommended that this is set to 'false'
# (as it is by default).
enforce-permission-checks-on-non-sponge-commands=false
# Patches the specified commands to respect the world of the sender instead of applying the
# changes on the all worlds.
multi-world-patches {
defaultgamemode=true
difficulty=true
gamerule=true
seed=true
setdefaultspawnpoint=true
time=true
toggledownfall=true
weather=true
worldborder=true
}
}
# Configuratiom options related to debugging features that are disabled by default.
debug {
# Detect and prevent parts of PlayerChunkMap being called off the main thread.
# This may decrease sever preformance, so you should only enable it when debugging a specific issue.
concurrent-chunk-map-checks=false
# Detect and prevent certain attempts to use entities concurrently.
# WARNING: May drastically decrease server performance.
# Only set this to 'true' to debug a pre-existing issue.
concurrent-entity-checks=false
# If 'true', Java's thread contention monitoring for thread dumps is enabled.
thread-contention-monitoring=false
}
# Configuration options related to entities and their performance impact.
entity {
# Number of colliding entities in one spot before logging a warning.
# Set to '0' to disable.
collision-warn-size=200
# Number of ticks before a painting is respawned on clients when their art is changed.
entity-painting-respawn-delay=2
# Number of ticks before the fake player entry of a human is removed from the tab list
# (ranges from '0' to '100' ticks).
human-player-list-remove-delay=10
# Controls the time in ticks for when an item despawns.
item-despawn-rate=6000
# The upper bounded range where living entities farther from a player will likely despawn.
living-hard-despawn-range=128
# The amount of seconds before a living entity between the soft and hard despawn ranges
# from a player to be considered for despawning.
living-soft-despawn-minimum-life=30
# The lower bounded range where living entities near a player may potentially despawn.
living-soft-despawn-range=32
# Maximum size of an entity's bounding box before removing it.
# Set to '0' to disable.
max-bounding-box-size=1000
# Square of the maximum speed of an entity before removing it.
# Set to '0' to disable.
max-speed=100
}
# Allows the configuration of the default entity activation ranges.
entity-activation-range {
# If 'true', newly discovered entities will be added to this config with a default value.
auto-populate=false
# Default activation ranges used for all entities unless overridden.
defaults {
ambient=32
aquatic=32
creature=32
misc=16
monster=32
}
# Per-mod overrides. Refer to the minecraft default mod for example.
mods {}
}
# Configuration options related to entity collision checks.
entity-collisions {
# If 'true', newly discovered entities/blocks will be added to this config with a default value.
auto-populate=false
# Maximum amount of entities any given entity or block can collide with.
# This improves performance when there are more than 8 entities on top of each other
# such as a 1x1 spawn pen.Set to '0' to disable.
max-entities-within-aabb=8
# Per-mod overrides. Refer to the minecraft default mod for example.
mods {
minecraft {
# Default maximum collisions used for all entities unless overridden.
defaults {}
# If 'false', entity collision rules for this mod will be ignored.
enabled=true
entities {}
}
}
}
# Configuration option related to sponge provided exploit patches
exploits {
# If 'limit-book-size' is enabled, controls the multiplier applied to each book page size
book-size-total-multiplier=0.98
# Enables filtering invalid entities when a chunk is being saved
# such that the entity that does not "belong" in the saving chunk will not be saved,
# and forced an update to the world's tracked entity lists for chunks.
# See https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0311-Prevent-Saving-Bad-entities-to-chunks.patch
filter-invalid-entities-on-chunk-save=true
# Limits the size of a book that can be sent by the client.
# See https://github.com/PaperMC/Paper/blob/f8058a8187da9f6185d95bb786783e12c79c8b18/Spigot-Server-Patches/0403-Book-Size-Limits.patch
# (Only affects SpongeVanilla)
limit-book-size=true
# Enables focing a chunk load when an entity position is set.
# Usually due to teleportation, vehicle movement etc. can a position lead an entity to no longer exist
# within it's currently marked and tracked chunk. This will enable that chunk for the position is loaded.
# Part of several exploits.
# See https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0335-Ensure-chunks-are-always-loaded-on-hard-position-set.patch
# (Only affects SpongeVanilla)
load-chunk-on-position-set=true
# Enables forcing chunks to save when an entity is added or removed from said chunk.
# This is a partial fix for some exploits using vehicles.
# See https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0306-Mark-chunk-dirty-anytime-entities-change-to-guarante.patch
# (Only affects SpongeVanilla)
mark-chunks-as-dirty-on-entity-list-modification=true
# If 'limit-book-size' is enabled, controls the maximum size of a book page
max-book-page-size=2560
# Prevents an exploit in which the client sends a packet with the itemstack name
# exceeding the string limit.
prevent-creative-itemstack-name-exploit=true
# Enables forcing updates to the player's location on vehicle movement.
# This is partially required to update the server's understanding of where the player exists,
# and allows chunk loading issues to be avoided with laggy connections and/or hack clients.
# See https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0378-Sync-Player-Position-to-Vehicles.patch
# (Only affects SpongeVanilla)
sync-player-positions-for-vehicle-movement=true
# Enables forcing a chunk-tracking refresh on entity movement.
# This enables a guarantee that the entity is tracked in the proper chunk when moving.
# See https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0315-Always-process-chunk-registration-after-moving.patch
# (Only affects SpongeVanilla)
update-tracked-chunk-on-entity-move=true
}
# Contains general configuration options for Sponge that don't fit into a specific classification
general {
# If 'true', Sponge will try to ensure that its data is on disk
# when saving the 'level_sponge.dat' file,
# but this may cause a slight performance hit.
#
# Setting this to 'false' will mean Sponge does not
# perform any consistency checks, but you may end up
# with corrupt data if an unexpected failure occurs on your server,
# requiring restoring this file from backup.
check-file-when-saving-sponge-data-file=true
# The directory for Sponge plugin configurations, relative to the
# execution root or specified as an absolute path.
# Note that the default: '${CANONICAL_GAME_DIR}/config'
# is going to use the 'config' directory in the root game directory.
# If you wish for plugin configs to reside within a child of the configuration
# directory, change the value to, for example, '${CANONICAL_CONFIG_DIR}/sponge/plugins'.
# Note: It is not recommended to set this to '${CANONICAL_CONFIG_DIR}/sponge', as there is
# a possibility that plugin configurations can conflict the Sponge core configurations.
#
config-dir="${CANONICAL_GAME_DIR}/config"
# If 'true', sleeping between chunk saves will be enabled, beware of memory issues.
file-io-thread-sleep=false
# Additional directory to search for plugins, relative to the
# execution root or specified as an absolute path.
# Note that the default: '${CANONICAL_MODS_DIR}/plugins'
# is going to search for a plugins folder in the mods directory.
# If you wish for the plugins folder to reside in the root game
# directory, change the value to '${CANONICAL_GAME_DIR}/plugins'.
plugins-dir="${CANONICAL_MODS_DIR}/plugins"
}
# Automatically assigns (permission) contexts to users that use any of the given ips.
# This can be used to restrict/grant permissions, based on the player's source or target ip.
ip-sets {}
# Configuration option related to logging certain action such as chunk loading.
logging {
# Log when blocks are broken
block-break=false
# Log when blocks are modified
block-modify=false
# Log when blocks are placed
block-place=false
# Log when blocks are populated in a chunk
block-populate=false
# Log when blocks are placed by players and tracked
block-tracking=false
# Log when chunks are queued to be unloaded by the chunk garbage collector.
chunk-gc-queue-unload=false
# Log when chunks are loaded
chunk-load=false
# Log when chunks are unloaded
chunk-unload=false
# Whether to log entity collision/count checks
entity-collision-checks=false
# Log when living entities are destroyed
entity-death=false
# Log when living entities are despawned
entity-despawn=false
# Log when living entities are spawned
entity-spawn=false
# Whether to log entity removals due to speed
entity-speed-removal=false
# Log when server receives exploited packet with itemstack name exceeding string limit.
exploit-itemstack-name-overflow=false
# Log when player attempts to respawn invisible to surrounding players.
exploit-respawn-invisibility=false
# Log when server receives exploited packet to update a sign containing commands
# from player with no permission.
exploit-sign-command-updates=false
# Add stack traces to dev logging
log-stacktraces=false
# Log when two conflicting changes are merged into one. (This number specifies the maximum number of
# messages to log. Set to 0 to show all messages.)
transaction-merge-fail=25
# Log when a world auto-saves its chunk data.
# Note: This may be spammy depending on the auto-save-interval configured for world.
world-auto-save=false
}
# Configuration options related to metric collection.
metrics {
# The global collection state that should be respected by all plugins that have no specified collection state. If 'undefined' then it is treated as disabled.
global-state=UNDEFINED
# Plugin-specific collection states that override the global collection state.
plugin-states {}
}
# Sponge provides a number of modules that allow for enabling or disabling
# certain specific features. These may be enabled or disabled below.
#
# Any changes here require a server restart as modules are applied at startup.
modules {
# Enables experimental fixes for broken mods
broken-mod=false
# The BungeeCord Module, allows Sponge to be used with a Proxy like Bungeecord or Velocity.
# Usually you want to also enable 'bungeecord.ip-forwarding' with this.
bungeecord=true
# The entity activation module, allows you to tweak entity activation ranges,
# similarly to "Entity Distance" in the latest 1.16 snapshots but per mob.
# Essentially, it changes how far an entity can be away from a player, until it stops being updated (e.g. moving).
entity-activation-range=true
# Module that allows you to configure the maximum amount of entities a specific entity or block can collide with.
entity-collisions=true
# Controls whether any exploit patches are applied.
# If there are issues with any specific exploits, please test in the exploit category first,
# before disabling all exploits with this toggle.
exploits=true
# Allows configuring Vanilla movement and speed checks
movement-checks=false
optimizations=true
# Use real (wall) time instead of ticks as much as possible
realtime=false
# Controls block range and tick rate of tileentities.
# Use with caution as this can break intended functionality.
tileentity-activation=false
# Module to run Aikar's Timings, profile your server performance and get a sharable web result.
# https://github.com/aikar/timings#aikars-minecraft-timings-viewer-v2
timings=true
# Can be used to disable some parts of the tracking.
tracking=true
}
# Configuration options related to minecraft's movement checks, that can be enabled or disabled.
movement-checks {
# Controls whether the 'player/entity moved wrongly!' check will be enforced
moved-wrongly=true
# Controls whether the 'player moved too quickly!' check will be enforced
player-moved-too-quickly=true
# Controls whether the 'vehicle of player moved too quickly!' check will be enforced
player-vehicle-moved-too-quickly=true
}
# Configuration options related to sponge provided performance optimizations.
optimizations {
# Runs lighting updates asynchronously.
async-lighting {
# If 'true', lighting updates are run asynchronously.
enabled=true
# The amount of threads to dedicate for asynchronous lighting updates. (Default: 2)
num-threads=2
}
# Caches tameable entities owners to avoid constant lookups against data watchers.
# If mods cause issues, disable this.
cache-tameable-owners=true
# Occasionally, some built in advancements, recipes, etc. can fail to deserialize properly
# which ends up potentially spamming the server log and the original provider of the failing content
# is not able to fix them. This provides an option to suppress the exceptions printing out in the log.
disable-failing-deserialization-log-spam=true
# In vanilla, pathfinding may result in loading chunks.
# You can disable that here, which may result in a
# performance improvement. This may not work well
# with mods.
disable-pathfinding-chunk-loads=false
# In vanilla, ray tracing may result in loading chunks.
# You can disable that here, which may result in a
# performance improvement. This may not work well
# with mods.
disable-raytracing-chunk-loads=false
# If 'true', block item drops are pre-processed to avoid
# having to spawn extra entities that will be merged post spawning.
# Usually, Sponge is smart enough to determine when to attempt an item pre-merge and when not to,
# however, in certain cases, some mods rely on items not being pre-merged and actually spawned,
# in which case, the items will flow right through without being merged.
drops-pre-merge=true
# Uses theosib's redstone algorithms to completely overhaul the way redstone works.
eigen-redstone {
# If 'true', uses theosib's redstone implementation which improves performance.
# See https://bugs.mojang.com/browse/MC-11193 and
# https://bugs.mojang.com/browse/MC-81098 for more information.
# Note: We cannot guarantee compatibility with mods. Use at your discretion.
enabled=false
# If 'true', restores the vanilla algorithm for computing wire power levels when powering off.
vanilla-decrement=false
# If 'true', restores the vanilla algorithm for propagating redstone wire changes.
vanilla-search=false
}
# If 'true', provides a fix for possible leaks through
# Minecraft's enchantment helper code that can leak
# entity and world references without much interaction
# Forge native (so when running SpongeForge implementation)
# has a similar patch, but Sponge's patch works a little harder
# at it, but Vanilla (SpongeVanilla implementation) does NOT
# have any of the patch, leading to the recommendation that this
# patch is enabled "for sure" when using SpongeVanilla implementation.
# See https://bugs.mojang.com/browse/MC-128547 for more information.
#
enchantment-helper-leak-fix=true
# If 'true', allows for Sponge to make better assumptinos on single threaded
# operations with relation to various checks for server threaded operations.
# This is default to true due to Sponge being able to precisely inject when
# the server thread is available. This should make an already fast operation
# much faster for better thread checks to ensure stability of sponge's systems.
faster-thread-checks=true
# If 'true', re-writes the incredibly inefficient Vanilla Map code.
# This yields enormous performance enhancements when using many maps,
# but has a tiny chance of breaking mods that invasively modify Vanilla.
# It is strongly reccomended to keep this on, unless explicitly advised otherwise by a Sponge developer
map-optimization=true
# Based on Aikar's optimizations of Hoppers, setting this to 'true'
# will allow for hoppers to save performing server -> client updates when transferring items.
# Because hoppers can transfer items multiple times per tick, these updates can get costly on the server,
# with little to no benefit to the client. Because of the nature of the change,
# the default will be 'false' due to the inability to pre-emptively
# foretell whether mod compatibility will fail with these changes or not.
# Refer to: https://github.com/PaperMC/Paper/blob/8175ec916f31dcd130fe0884fe46bdc187d829aa/Spigot-Server-Patches/0269-Optimize-Hoppers.patch
# for more details.
optimize-hoppers=false
# If 'true', uses Panda4494's redstone implementation which improves performance.
# See https://bugs.mojang.com/browse/MC-11193 for more information.
# Note: This optimization has a few issues which are explained in the bug report.
# We strongly recommend using eigen redstone over this implementation as this will
# be removed in a future release.
panda-redstone=false
# Handles structures that are saved to disk. Certain structures can take up large amounts
# of disk space for very large maps and the data for these structures is only needed while the
# world around them is generating. Disabling saving of these structures can save disk space and
# time during saves if your world is already fully generated.
# Warning: disabling structure saving will break the vanilla locate command.
structure-saving {
# If 'true', newly discovered structures will be added to this config with a default value of 'true'.
# This is useful for finding out potentially what structures are being saved from various mods,
# and allowing those structures to be selectively disabled.
auto-populate=false
# Global switch to enable sponge's changes to the structure saving mechansim.
# By default, this option is disabled, i.e. saving structures is not affected.
# If you want to prevent that a certain "named" structure is saved to the world's folder,
# you have to enable this module/setting and disable the structure in the further settings.
# An example of a structure that is costly and somewhat irrelevant is 'mineshaft',
# as they build and save several structures even after the mine shafts have been completely generated.
# However, this has the disadvantage that these structures may no longer be locatable by some mods.
enabled=false
# Per-mod overrides. Refer to the minecraft default mod for example.
mods {
minecraft {
# Global flag, whether this mod's structures will be saved.
# If 'false', this mod will never save its structures.
# If 'true', it will check the 'structureList' for disabled structures.
enabled=true
# The configuration for each struture.
# A value of 'false' prevents that struture from being saved.
# Entries that are either missing in this list or have the value 'true' will still be saved,
# unless the structure saving of the mod is globally disabled.
structures {
mineshaft=false
}
}
}
}
# Vanilla performs a lot of is area loaded checks during entity collision calculations with blocks,
# and because these calculations require fetching the chunks to see if they are loaded,
# before getting the block states from those chunks, there can be some small performance
# increase by checking the entity's owned active chunk it may currently reside in.
# Essentially, instead of asking the world if those chunks are loaded, the entity
# would know whether it's chunks are loaded and that neighbor's chunks are loaded.
use-active-chunks-for-collisions=false
}
# Configuration options related to permissions and permission handling
permission {
# If 'true', Sponge plugins will be used to handle permissions rather than any Forge mod
forge-permissions-handler=false
}
# Configuration options related to tracking player interactions with blocks
player-block-tracker {
# Block IDs that will be blacklisted for player block placement tracking.
block-blacklist=[]
# If 'true', adds player tracking support for block positions.
# Note: This should only be disabled if you do not care who caused a block to change.
enabled=true
}
# Used to control spawn limits around players.
# Note: The radius uses the lower value of mob spawn range and server's view distance.
spawner {
# The number of ambients the spawner can potentially spawn around a player.
spawn-limit-ambient=15
# The number of animals the spawner can potentially spawn around a player.
spawn-limit-animal=15
# The number of aquatics the spawner can potentially spawn around a player.
spawn-limit-aquatic=5
# The number of monsters the spawner can potentially spawn around a player.
spawn-limit-monster=70
# The ambient spawning tick rate. Default: 400
tick-rate-ambient=400
# The animal spawning tick rate. Default: 400
tick-rate-animal=400
# The aquatic spawning tick rate. Default: 1
tick-rate-aquatic=1
# The monster spawning tick rate. Default: 1
tick-rate-monster=1
}
# Configuration options related to the Sql service, including connection aliases etc
sql {
# Aliases for SQL connections, in the format jdbc:protocol://[username[:password]@]host/database
aliases {}
}
# Blocks to blacklist for safe teleportation.
teleport-helper {
# If 'true', this blacklist will always be respected, otherwise, plugins can choose whether
# or not to respect it.
force-blacklist=false
# Block IDs that are listed here will not be selected by Sponge's safe teleport routine as
# a safe block for players to warp into.
# You should only list blocks here that are incorrectly selected, solid blocks that prevent
# movement are automatically excluded.
unsafe-body-block-ids=[]
# Block IDs that are listed here will not be selected by Sponge's safe
# teleport routine as a safe floor block.
unsafe-floor-block-ids=[]
}
# Configuration options related to activation ranges of tile entities.
tileentity-activation {
# If 'true', newly discovered tileentities will be added to this config with default settings.
auto-populate=false
# Default activation block range used for all tileentities unless overridden.
default-block-range=64
# Default tick rate used for all tileentities unless overridden.
default-tick-rate=1
# Per-mod overrides. Refer to the minecraft default mod for example.
mods {}
}
# Module to run Aikar's Timings, profile your server performance and get a sharable web result.
# https://github.com/aikar/timings#aikars-minecraft-timings-viewer-v2
timings {
# Enables the timing module
enabled=true
# These configuration entries/paths/sections are removed before the report is sent.
# This is to prevent credentials from being leaked unintentionally.
# Note: The 'sponge.sql' section is always ignored
# and thus is never sent to the webviewer regardless of this config.
hidden-config-entries=[
"sponge.sql"
]
# Modulo value how often a tick should be pushed to the timings history
history-interval=300
# How long the list of said history can get
history-length=3600
# Hides the server name in the Aikar webviewer to no leak the l33t server you are running
server-name-privacy=false
# Enables the verbose mode for the timings module.
verbose=false
}
# Configuration options that will affect all worlds.
world {
# The auto-save tick interval used when saving global player data. (Default: 900)
# Note: 20 ticks is equivalent to 1 second. Set to 0 to disable.
auto-player-save-interval=900
# The auto-save tick interval used to save all loaded chunks in a world.
# Set to 0 to disable. (Default: 900)
# Note: 20 ticks is equivalent to 1 second.
auto-save-interval=900
# The number of newly loaded chunks before triggering a forced cleanup.
# Note: When triggered, the loaded chunk threshold will reset and start incrementing.
# Disabled by default.
chunk-gc-load-threshold=0
# The tick interval used to cleanup all inactive chunks that have leaked in a world.
# Set to 0 to disable which restores vanilla handling. (Default: 600)
chunk-gc-tick-interval=600
# The number of seconds to delay a chunk unload once marked inactive. (Default: 15)
# Note: This gets reset if the chunk becomes active again.
chunk-unload-delay=15
# If 'true', any request for a chunk not currently loaded will be denied
# (exceptions apply for things like world gen and player movement).
# Warning: As this is an experimental setting for performance gain,
# if you encounter any issues then we recommend disabling it.
deny-chunk-requests=true
# If 'true', any neighbour notification for a chunk not currently loaded will be denied
# Warning: As this is an experimental setting for performance gain,
# if you encounter any issues then we recommend disabling it.
deny-neighbor-notification-chunk-requests=false
# The interval, in seconds, used by the GameProfileQueryTask to process queued GameProfile requests. (Default: 4)
# Note: This setting should be raised if you experience the following error:
# "The client has sent too many requests within a certain amount of time".
# Finally, if set to 0 or less, the default interval will be used.
gameprofile-lookup-task-interval=4
# If 'true', this world will generate its spawn the moment its loaded.
generate-spawn-on-load=false
# The list of uuid's that should never perform a lookup against Mojang's session server.
# Note: If you are using SpongeForge, make sure to enter any mod fake player's UUID to this list.
invalid-lookup-uuids=[
"00000000-0000-0000-0000-000000000000",
"0d0c4ca0-4ff1-11e4-916c-0800200c9a66",
"41c82c87-7afb-4024-ba57-13d2c99cae77"
]
# The defined merge radius for Item entities such that when two items are
# within the defined radius of each other, they will attempt to merge.
# Usually, the default radius is set to 0.5 in Vanilla, however, for performance reasons
# 2.5 is generally acceptable.
# Note: Increasing the radius higher will likely cause performance degradation
# with larger amount of items as they attempt to merge and search nearby
# areas for more items. Setting to a negative value is not supported!
item-merge-radius=2.5
# If 'true', this worlds spawn will remain loaded with no players.
keep-spawn-loaded=true
# If 'true', natural leaf decay is allowed.
leaf-decay=true
# If 'true', this world will load on startup.
load-on-startup=false
# The maximum number of queued unloaded chunks that will be unloaded in a single tick.
# Note: With the chunk gc enabled, this setting only applies to the ticks
# where the gc runs (controlled by 'chunk-gc-tick-interval')
# Note: If the maximum unloads is too low, too many chunks may remain loaded on the world
# and increases the chance for a drop in tps. (Default: 100)
max-chunk-unloads-per-tick=100
# Specifies the radius (in chunks) of where creatures will spawn.
# This value is capped to the current view distance setting in server.properties
mob-spawn-range=4
# A list of all detected portal agents used in this world.
# In order to override, change the target world name to any other valid world.
# Note: If world is not found, it will fallback to default.
portal-agents {
"minecraft:default_the_end"=DIM1
"minecraft:default_the_nether"=DIM-1
}
# If 'true', this world will allow PVP combat.
pvp-enabled=true
# Override world distance per world/dimension
# The value must be greater than or equal to 3 and less than or equal to 32
# The server-wide view distance will be used when the value is -1.
view-distance=-1
# If 'true', natural formation of ice and snow in supported biomes will be allowed.
weather-ice-and-snow=true
# If 'true', thunderstorms will be initiated in supported biomes.
weather-thunder=true
# If 'true', this world will be registered.
world-enabled=true
}
# World Generation Modifiers to apply to the world
world-generation-modifiers=[]
}
+76
View File
@@ -0,0 +1,76 @@
# 1.0
#
# # If you need help with the configuration or have any questions related to Sponge,
# # join us at the IRC or drop by our forums and leave a post.
#
# # IRC: #sponge @ irc.esper.net ( https://webchat.esper.net/?channel=sponge )
# # Forums: https://forums.spongepowered.org/
#
sponge {
block {
# If 'true', newly discovered blocks will be added to this config with default settings.
auto-populate=false
# Per-mod block id mappings for controlling tracking behavior
mods {
extrautils2 {
# Set to true to perform block bulk capturing during block ticks. (Default: true)
block-bulk-capture {
redstoneclock=false
}
# Set to true to create and fire block events during block ticks. (Default: true)
block-event-creation {}
# If 'false', all tracking for this mod will be ignored.
enabled=true
# Set to true to create and fire entity events during block ticks. (Default: true)
entity-block-creation {}
# Set to true to perform entity bulk capturing during block ticks. (Default: true)
entity-bulk-capture {}
}
minecraft {
# Set to true to perform block bulk capturing during block ticks. (Default: true)
block-bulk-capture {}
# Set to true to create and fire block events during block ticks. (Default: true)
block-event-creation {}
# If 'false', all tracking for this mod will be ignored.
enabled=true
# Set to true to create and fire entity events during block ticks. (Default: true)
entity-block-creation {}
# Set to true to perform entity bulk capturing during block ticks. (Default: true)
entity-bulk-capture {}
}
}
}
entity {
# If 'true', newly discovered entities will be added to this config with default settings.
auto-populate=false
# Per-mod entity id mappings for controlling tracking behavior
mods {
minecraft {
# Set to true to perform block bulk capturing during entity ticks. (Default: true)
block-bulk-capture {
"armor_stand"=false
"experience_orb"=false
item=false
"leash_hitch"=false
"llama_spit"=false
painting=false
}
# Set to true to create and fire block events during entity ticks. (Default: true)
block-event-creation {}
# If 'false', all tracking for this mod will be ignored.
enabled=true
# Set to true to create and fire entity events during entity ticks. (Default: true)
entity-block-creation {}
# Set to true to perform entity bulk capturing during entity ticks. (Default: true)
entity-bulk-capture {}
}
}
}
tileentity {
# If 'true', newly discovered tileentities will be added to this config with default settings.
auto-populate=false
# Per-mod tileentity id mappings for controlling tracking behavior
mods {}
}
}
@@ -0,0 +1,16 @@
# 1.0
#
# # If you need help with the configuration or have any questions related to Sponge,
# # join us at the IRC or drop by our forums and leave a post.
#
# # IRC: #sponge @ irc.esper.net ( https://webchat.esper.net/?channel=sponge )
# # Forums: https://forums.spongepowered.org/
#
sponge {
# Configuration options that will affect all worlds.
world {
# If 'true', this world will load on startup.
load-on-startup=true
}
}
@@ -0,0 +1,10 @@
# 1.0
#
# # If you need help with the configuration or have any questions related to Sponge,
# # join us at the IRC or drop by our forums and leave a post.
#
# # IRC: #sponge @ irc.esper.net ( https://webchat.esper.net/?channel=sponge )
# # Forums: https://forums.spongepowered.org/
#
@@ -0,0 +1,10 @@
# 1.0
#
# # If you need help with the configuration or have any questions related to Sponge,
# # join us at the IRC or drop by our forums and leave a post.
#
# # IRC: #sponge @ irc.esper.net ( https://webchat.esper.net/?channel=sponge )
# # Forums: https://forums.spongepowered.org/
#
@@ -0,0 +1,16 @@
# 1.0
#
# # If you need help with the configuration or have any questions related to Sponge,
# # join us at the IRC or drop by our forums and leave a post.
#
# # IRC: #sponge @ irc.esper.net ( https://webchat.esper.net/?channel=sponge )
# # Forums: https://forums.spongepowered.org/
#
sponge {
# Configuration options that will affect all worlds.
world {
# If 'true', this world will load on startup.
load-on-startup=true
}
}
@@ -0,0 +1,10 @@
# 1.0
#
# # If you need help with the configuration or have any questions related to Sponge,
# # join us at the IRC or drop by our forums and leave a post.
#
# # IRC: #sponge @ irc.esper.net ( https://webchat.esper.net/?channel=sponge )
# # Forums: https://forums.spongepowered.org/
#
+136
View File
@@ -0,0 +1,136 @@
# Thanks for downloading ViaVersion
# Ensure you look through all these options
# If you need help:
# Discord - https://viaversion.com/discord
# viaversion.com - Discussion tab
# Docs - https://docs.viaversion.com/display/VIAVERSION/Configuration
#
# ----------------------------------------------------------#
# GLOBAL OPTIONS #
# ----------------------------------------------------------#
#
# Should ViaVersion check for updates?
checkforupdates: true
# Send the supported versions with the Status (Ping) response packet
send-supported-versions: false
# Block specific Minecraft protocols that ViaVersion allows
# List of all Minecraft protocol versions: http://wiki.vg/Protocol_version_numbers or use a generator: https://via.krusic22.com
block-protocols: []
# Change the blocked disconnect message
block-disconnect-msg: You are using an unsupported Minecraft version!
# If you use ProtocolLib, we can't reload without kicking the players.
# (We don't suggest using reload either, use a plugin manager)
# You can customise the message we kick people with if you use ProtocolLib here.
reload-disconnect-msg: Server reload, please rejoin!
# We warn when there's a error converting item and block data over versions, should we suppress these? (Only suggested if spamming)
suppress-conversion-warnings: false
#
# ----------------------------------------------------------#
# GLOBAL PACKET LIMITER #
# ----------------------------------------------------------#
#
#
# Packets Per Second (PPS) limiter (Use -1 on max-pps and tracking-period to disable)
# Clients by default send around 20-90 packets per second.
#
# What is the maximum per second a client can send (Use %pps to display their pps)
# Use -1 to disable.
max-pps: 800
max-pps-kick-msg: You are sending too many packets!
#
# We can also kick them if over a period they send over a threshold a certain amount of times.
#
# Period to track (in seconds)
# Use -1 to disable.
tracking-period: 6
# How many packets per second counts as a warning
tracking-warning-pps: 120
# How many warnings over the interval can we have
# This can never be higher than "tracking-period"
tracking-max-warnings: 4
tracking-max-kick-msg: You are sending too many packets, :(
#
# ----------------------------------------------------------#
# MULTIPLE VERSIONS OPTIONS #
# ----------------------------------------------------------#
#
# Should we enable our hologram patch?
# If they're in the wrong place enable this
hologram-patch: false
# This is the offset, should work as default when enabled.
hologram-y: -0.96
# Should we disable piston animation for 1.11/1.11.1 clients?
# In some cases when firing lots of pistons it crashes them.
piston-animation-patch: false
# Should we fix nbt for 1.12 and above clients in chat messages (causes invalid item)
chat-nbt-fix: true
# Should we use prefix for team colour on 1.13 and above clients
team-colour-fix: true
# 1.13 introduced new auto complete which can trigger "Kicked for spamming" for servers older than 1.13, the following option will disable it completely.
disable-1_13-auto-complete: false
# The following option will delay the tab complete request in x ticks if greater than 0, if other tab-complete is received, the previous is cancelled
1_13-tab-complete-delay: 0
# For 1.13 clients the smallest (1 layer) snow doesn't have collision, this will send these as 2 snowlayers for 1.13+ clients to prevent them bugging through them
fix-low-snow-collision: false
# Infested blocks are instantly breakable for 1.13+ clients, resulting in them being unable to break them on sub 1.13 servers. This remaps them to their normal stone variants
fix-infested-block-breaking: true
# In 1.14 the client page limit has been upped to 100 (from 50). Some anti-exploit plugins ban when clients go higher than 50. This option cuts edited books to 50 pages.
truncate-1_14-books: false
# Fixes 1.14+ clients on sub 1.14 servers having a light value of 0 for non full blocks.
fix-non-full-blocklight: true
# Fixes walk animation not shown when health is set to Float.NaN
fix-1_14-health-nan: true
# Should 1.15+ clients respawn instantly / without showing a death screen?
use-1_15-instant-respawn: false
#
# Enable serverside block-connections for 1.13+ clients - all of the options in this section are built around this option
serverside-blockconnections: true
# When activated, only the most important blocks are stored in the blockstorage. (fences, glass panes etc. won't connect to solid blocks)
reduce-blockstorage-memory: false
# When activated with serverside-blockconnections, flower parts with blocks above will be sent as stems
# Useful for lobbyservers where users can't build and those stems are used decoratively
flowerstem-when-block-above: false
# Vines that are not connected to blocks will be mapped to air, else 1.13+ would still be able to climb up on them.
vine-climb-fix: false
#
# Ignores incoming plugin channel messages of 1.16+ clients with channel names longer than 32 characters.
# CraftBukkit had this limit hardcoded until 1.16, so we have to assume any server/proxy might have this arbitrary check present.
ignore-long-1_16-channel-names: true
#
# ----------------------------------------------------------#
# 1.9+ CLIENTS ON 1.8 SERVERS OPTIONS #
# ----------------------------------------------------------#
#
# No collide options, these allow you to configure how collision works.
# Do you want us to prevent collision?
prevent-collision: true
# If the above is true, should we automatically team players until you do?
auto-team: true
# When enabled if certain metadata can't be read we won't tell you about it
suppress-metadata-errors: false
# When enabled 1.9+ will be able to block by using shields
shield-blocking: true
# Enable player tick simulation, this fixes eating, drinking, nether portals.
simulate-pt: true
# Should we use nms player to simulate packets, (may fix anti-cheat issues)
nms-player-ticking: true
# Should we patch boss bars so they work? (Default: true, disable if you're having issues)
bossbar-patch: true
# If your boss bar flickers on 1.9+, set this to 'true'. It will keep all boss bars on 100% (not recommended)
bossbar-anti-flicker: false
# This will show the new effect indicator in the top-right corner for 1.9+ players.
use-new-effect-indicator: true
# Show the new death messages for 1.9+ on the death screen
use-new-deathmessages: true
# Should we cache our items, this will prevent server from being lagged out, however the cost is a constant task caching items
item-cache: true
# Should we replace extended pistons to fix 1.10.1 (Only on chunk load)
replace-pistons: false
# What id should we replace with, default is air. (careful of players getting stuck standing on them)
replacement-piston-id: 0
# Force the string -> json transform
force-json-transform: false
# Minimize the cooldown animation in 1.8 servers
minimize-cooldown: true
# Left handed handling on 1.8 servers
left-handed-handling: true
+4
View File
@@ -0,0 +1,4 @@
#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula).
#You also agree that tacos are tasty, and the best food in the world.
#Tue Jun 16 08:43:06 EDT 2020
eula=true
-56
View File
@@ -1,56 +0,0 @@
# This is the help configuration file for Bukkit.
#
# By default you do not need to modify this file. Help topics for all plugin commands are automatically provided by
# or extracted from your installed plugins. You only need to modify this file if you wish to add new help pages to
# your server or override the help pages of existing plugin commands.
#
# This file is divided up into the following parts:
# -- general-topics: lists admin defined help topics
# -- index-topics: lists admin defined index topics
# -- amend-topics: lists topic amendments to apply to existing help topics
# -- ignore-plugins: lists any plugins that should be excluded from help
#
# Examples are given below. When amending command topic, the string <text> will be replaced with the existing value
# in the help topic. Color codes can be used in topic text. The color code character is & followed by 0-F.
# ================================================================
#
# Set this to true to list the individual command help topics in the master help.
# command-topics-in-master-index: true
#
# Each general topic will show up as a separate topic in the help index along with all the plugin command topics.
# general-topics:
# Rules:
# shortText: Rules of the server
# fullText: |
# &61. Be kind to your fellow players.
# &B2. No griefing.
# &D3. No swearing.
# permission: topics.rules
#
# Each index topic will show up as a separate sub-index in the help index along with all the plugin command topics.
# To override the default help index (displayed when the user executes /help), name the index topic "Default".
# index-topics:
# Ban Commands:
# shortText: Player banning commands
# preamble: Moderator - do not abuse these commands
# permission: op
# commands:
# - /ban
# - /ban-ip
# - /banlist
#
# Topic amendments are used to change the content of automatically generated plugin command topics.
# amended-topics:
# /stop:
# shortText: Stops the server cold....in its tracks!
# fullText: <text> - This kills the server.
# permission: you.dont.have
#
# Any plugin in the ignored plugins list will be excluded from help. The name must match the name displayed by
# the /plugins command. Ignore "Bukkit" to remove the standard bukkit commands from the index. Ignore "All"
# to completely disable automatic help topic generation.
# ignore-plugins:
# - PluginNameOne
# - PluginNameTwo
# - PluginNameThree
File renamed without changes.
+1
View File
@@ -0,0 +1 @@
../../../universal/build/libs/nuvotifier.jar
+1 -1
View File
@@ -1 +1 @@
../resources/paper.jar
../resources/sponge.jar
-178
View File
@@ -1,178 +0,0 @@
# This is the main configuration file for Paper.
# As you can see, there's tons to configure. Some options may impact gameplay, so use
# with caution, and make sure you know what each option does before configuring.
#
# If you need help with the configuration or have any questions related to Paper,
# join us in our Discord or IRC channel.
#
# Discord: https://paperdiscord.emc.gs
# IRC: #paper @ irc.spi.gt ( http://irc.spi.gt/iris/?channels=paper )
# Website: https://papermc.io/
# Docs: https://paper.readthedocs.org/
verbose: false
config-version: 16
settings:
load-permissions-yml-before-plugins: true
bungee-online-mode: true
incoming-packet-spam-threshold: 300
use-versioned-world: false
velocity-support:
enabled: false
online-mode: false
secret: ''
queue-light-updates-max-loss: 10
suggest-player-names-when-null-tab-completions: true
watchdog:
early-warning-every: 5000
early-warning-delay: 10000
spam-limiter:
tab-spam-increment: 1
tab-spam-limit: 500
save-player-data: true
sleep-between-chunk-saves: false
region-file-cache-size: 256
save-empty-scoreboard-teams: false
book-size:
page-max: 2560
total-multiplier: 0.98
enable-player-collisions: true
player-auto-save-rate: -1
max-player-auto-save-per-tick: -1
async-chunks:
enable: true
generation: true
thread-per-world-generation: true
load-threads: -1
use-alternative-luck-formula: false
messages:
no-permission: '&cI''m sorry, but you do not have permission to perform this command.
Please contact the server administrators if you believe that this is in error.'
kick:
authentication-servers-down: ''
connection-throttle: Connection throttled! Please wait before reconnecting.
flying-player: Flying is not enabled on this server
flying-vehicle: Flying is not enabled on this server
timings:
enabled: true
verbose: true
server-name-privacy: false
hidden-config-entries:
- database
- settings.bungeecord-addresses
history-interval: 300
history-length: 3600
world-settings:
default:
portal-search-radius: 128
game-mechanics:
villages-load-chunks: false
scan-for-legacy-ender-dragon: true
disable-chest-cat-detection: false
disable-end-credits: false
disable-player-crits: false
disable-sprint-interruption-on-attack: false
shield-blocking-delay: 5
disable-unloaded-chunk-enderpearl-exploit: true
queue-light-updates: false
optimize-explosions: false
disable-teleportation-suffocation-check: false
fixed-chunk-inhabited-time: -1
use-vanilla-world-scoreboard-name-coloring: false
delay-chunk-unloads-by: 10s
max-auto-save-chunks-per-tick: 24
save-queue-limit-for-auto-save: 50
enable-treasure-maps: true
treasure-maps-return-already-discovered: false
experience-merge-max-value: -1
max-chunk-sends-per-tick: 81
max-chunk-gens-per-tick: 10
prevent-moving-into-unloaded-chunks: false
max-growth-height:
cactus: 3
reeds: 3
fishing-time-range:
MinimumTicks: 100
MaximumTicks: 600
despawn-ranges:
soft: 32
hard: 128
falling-block-height-nerf: 0
tnt-entity-height-nerf: 0
remove-corrupt-tile-entities: false
lightning-strike-distance-limit:
sound: -1
impact-sound: -1
flash: -1
frosted-ice:
enabled: true
delay:
min: 20
max: 40
lootables:
auto-replenish: false
restrict-player-reloot: true
reset-seed-on-fill: true
max-refills: -1
refresh-min: 12h
refresh-max: 2d
filter-nbt-data-from-spawn-eggs-and-related: true
max-entity-collisions: 8
disable-creeper-lingering-effect: false
hopper:
cooldown-when-full: true
disable-move-event: false
duplicate-uuid-resolver: saferegen
duplicate-uuid-saferegen-delete-range: 32
keep-spawn-loaded: true
anti-xray:
enabled: false
engine-mode: 1
chunk-edge-mode: 1
max-chunk-section-index: 3
update-radius: 2
hidden-blocks:
- gold_ore
- iron_ore
- coal_ore
- lapis_ore
- mossy_cobblestone
- obsidian
- chest
- diamond_ore
- redstone_ore
- lit_redstone_ore
- clay
- emerald_ore
- ender_chest
replacement-blocks:
- stone
- planks
skip-entity-ticking-in-chunks-scheduled-for-unload: true
armor-stands-do-collision-entity-lookups: true
keep-spawn-loaded-range: 8
auto-save-interval: -1
disable-thunder: false
skeleton-horse-thunder-spawn-chance: 0.01
disable-ice-and-snow: false
fire-physics-event-for-redstone: false
grass-spread-tick-rate: 1
water-over-lava-flow-speed: 5
bed-search-radius: 1
use-faster-eigencraft-redstone: false
nether-ceiling-void-damage: false
allow-non-player-entities-on-scoreboards: false
container-update-tick-rate: 1
parrots-are-unaffected-by-player-movement: false
prevent-tnt-from-moving-in-water: false
non-player-arrow-despawn-rate: -1
creative-arrow-despawn-rate: -1
disable-explosion-knockback: false
armor-stands-tick: true
spawner-nerfed-mobs-should-jump: false
allow-leashing-undead-horse: false
baby-zombie-movement-speed: 0.5
all-chunks-are-slime-chunks: false
mob-spawner-tick-rate: 1
squid-spawn-height:
maximum: 0.0
-1
View File
@@ -1 +0,0 @@
rsa/
-29
View File
@@ -1,29 +0,0 @@
# The IP to listen to. Use 0.0.0.0 if you wish to listen to all interfaces on your server. (All IP addresses)
# This defaults to the IP you have configured your server to listen on, or 0.0.0.0 if you have not configured this.
host: 0.0.0.0
# Port to listen for new votes on
port: 8194
# Whether or not to print debug messages. In a production system, this should be set to false.
# This is useful when initially setting up NuVotifier to ensure votes are being delivered.
debug: true
# Setting this option to true will disable handling of Protocol v1 packets. While the old protocol is not secure, this
# option is currently not recommended as most voting sites only support the old protocol at present. However, if you are
# using NuVotifier's proxy forwarding mechanism, enabling this option will increase your server's security.
disable-v1-protocol: false
# All tokens, labeled by the serviceName of each server list.
tokens:
# Default token for all server lists, if another isn't supplied.
default: server2token
# Configuration section for all vote forwarding to NuVotifier
forwarding:
# Sets whether to set up a remote method for fowarding. Supported methods:
# - none - Does not set up a forwarding method.
# - pluginMessaging - Sets up plugin messaging
method: pluginMessaging
pluginMessaging:
channel: nuvotifier:votes
+13 -5
View File
@@ -1,5 +1,8 @@
#Minecraft server properties
#Fri Dec 21 15:15:00 EST 2018
#Thu Sep 03 15:49:48 EDT 2020
spawn-protection=16
max-tick-time=60000
query.port=25565
generator-settings=
force-gamemode=false
allow-nether=true
@@ -8,19 +11,23 @@ gamemode=0
broadcast-console-to-ops=true
enable-query=false
player-idle-timeout=0
difficulty=1
difficulty=2
spawn-monsters=true
broadcast-rcon-to-ops=true
op-permission-level=4
pvp=true
snooper-enabled=true
level-type=DEFAULT
level-type=default
hardcore=false
enable-command-block=false
max-players=20
network-compression-threshold=256
resource-pack-sha1=
max-world-size=29999984
function-permission-level=2
rcon.port=25575
server-port=25566
debug=false
server-ip=
spawn-npcs=true
allow-flight=false
@@ -29,11 +36,12 @@ view-distance=10
resource-pack=
spawn-animals=true
white-list=false
rcon.password=
generate-structures=true
online-mode=false
max-build-height=256
online-mode=false
level-seed=
prevent-proxy-connections=false
use-native-transport=true
prevent-proxy-connections=false
motd=A Minecraft Server
enable-rcon=false
-128
View File
@@ -1,128 +0,0 @@
# This is the main configuration file for Spigot.
# As you can see, there's tons to configure. Some options may impact gameplay, so use
# with caution, and make sure you know what each option does before configuring.
# For a reference for any variable inside this file, check out the Spigot wiki at
# http://www.spigotmc.org/wiki/spigot-configuration/
#
# If you need help with the configuration or have any questions related to Spigot,
# join us at the IRC or drop by our forums and leave a post.
#
# IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )
# Forums: http://www.spigotmc.org/
config-version: 11
settings:
debug: false
save-user-cache-on-stop-only: false
sample-count: 12
timeout-time: 60
restart-on-crash: true
restart-script: ./start.sh
netty-threads: 4
attribute:
maxHealth:
max: 2048.0
movementSpeed:
max: 2048.0
attackDamage:
max: 2048.0
bungeecord: true
late-bind: false
player-shuffle: 0
user-cache-size: 1000
moved-wrongly-threshold: 0.0625
moved-too-quickly-multiplier: 10.0
messages:
whitelist: You are not whitelisted on this server!
unknown-command: Unknown command. Type "/help" for help.
server-full: The server is full!
outdated-client: Outdated client! Please use {0}
outdated-server: Outdated server! I'm still on {0}
restart: Server is restarting
stats:
disable-saving: false
forced-stats: {}
commands:
tab-complete: 0
send-namespaced: true
log: true
spam-exclusions:
- /skill
silent-commandblock-console: false
replace-commands:
- setblock
- summon
- testforblock
- tellraw
advancements:
disable-saving: false
disabled:
- minecraft:story/disabled
world-settings:
default:
verbose: true
random-light-updates: false
dragon-death-sound-radius: 0
seed-village: 10387312
seed-desert: 14357617
seed-igloo: 14357618
seed-jungle: 14357619
seed-swamp: 14357620
seed-monument: 10387313
seed-shipwreck: 165745295
seed-ocean: 14357621
seed-slime: 987234911
hunger:
jump-walk-exhaustion: 0.05
jump-sprint-exhaustion: 0.2
combat-exhaustion: 0.1
regen-exhaustion: 6.0
swim-multiplier: 0.01
sprint-multiplier: 0.1
other-multiplier: 0.0
max-tnt-per-tick: 100
max-tick-time:
tile: 50
entity: 50
squid-spawn-range:
min: 45.0
merge-radius:
exp: 3.0
item: 2.5
mob-spawn-range: 8
growth:
cactus-modifier: 100
cane-modifier: 100
melon-modifier: 100
mushroom-modifier: 100
pumpkin-modifier: 100
sapling-modifier: 100
wheat-modifier: 100
netherwart-modifier: 100
vine-modifier: 100
cocoa-modifier: 100
entity-activation-range:
animals: 32
monsters: 32
misc: 16
water: 16
tick-inactive-villagers: true
entity-tracking-range:
players: 48
animals: 48
monsters: 48
misc: 32
other: 64
ticks-per:
hopper-transfer: 8
hopper-check: 1
hopper-amount: 1
wither-spawn-sound-radius: 0
enable-zombie-pigmen-portal-spawns: true
item-despawn-rate: 6000
view-distance: 10
arrow-despawn-rate: 1200
zombie-aggressive-towards-villager: true
hanging-tick-frequency: 100
nerf-spawner-mobs: false
max-entity-collisions: 8
-10
View File
@@ -1,10 +0,0 @@
# Changing the version of NuVotifier
So since I keep forgetting, here are the places which need to have their version
numbers changed:
+ build.gradle
+ com.vexsoftware.votifier.sponge.VotifierPlugin @Plugin
+ com.vexsoftware.votifier.velocity.VotifierPlugin @Plugin
This is really annoying.
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff. Show more