Compare commits

...
34 Commits
Author SHA1 Message Date
Tux f8019debd0 Releasing 2.3.4 2017-02-19 21:48:59 -05:00
Tux ab317e76b3 Catch the correct exception 2017-02-19 21:40:46 -05:00
Tux 15556be265 Better loading code 2017-02-19 21:39:08 -05:00
Tux 26958e35fe Don't attempt to "deduplicate" cached votes.
This is in line with original Votifier behavior.
2017-01-15 18:26:19 -05:00
Tux e905881238 Log successful vote attempt IPs 2017-01-15 02:44:43 -05:00
Tux fde7a941b6 Update usage document 2016-12-19 18:49:23 -05:00
Tux 9d24deabe9 Temporarily alleviate issues with large file caches
See #14
2016-12-17 21:32:19 -05:00
Tux c2f331e7ec Updated dependencies.
Bukkit 1.7.2 -> Spigot 1.8.8
SpongeAPI 4.1 -> SpongeAPI 5.0.0
2016-12-08 23:01:32 -05:00
Joseph Hirschfeld 705bddd062 Update sponge API version to 4.1.0 2016-08-15 19:40:48 -04:00
Joseph Hirschfeld 826609db30 Update nuvotifer to 2.3.3 2016-08-15 19:27:11 -04:00
Joseph Hirschfeld 0eca96bd67 Fix null file vote cache issues 2016-08-15 19:20:03 -04:00
Tux f767387840 Implement constant-time comparison 2016-06-25 11:32:19 -04:00
Tux 8ae3c21a23 Read key with UTF8 encoding 2016-06-25 11:30:11 -04:00
Tux be1c056502 Try fixing onlineServer forwarding again 2016-04-03 20:01:35 -04:00
Tux 500142eb5b add validation 2016-03-31 22:11:02 -04:00
Tux 1e5876bc08 Use try-finally 2016-03-26 23:57:00 -04:00
Tux 9355d6015f fix #7 2016-03-24 15:50:06 -04:00
Tux 700db6dca0 Add standalone server support 2016-03-22 21:41:04 -04:00
Tux f61ba7a6ad fix depend 2016-03-08 08:09:16 -05:00
Tux fe7e8cef41 SpongeAPI 4.x 2016-03-08 08:07:22 -05:00
Tux 7cda39ad12 fix travis 2016-03-05 03:24:37 -05:00
Tux b377a53de4 Fix vote detection 2016-03-05 01:15:57 -05:00
Tux 9a306365b8 2.3.2.2 2016-03-05 00:50:39 -05:00
Tux f74cdbe648 Read and drain the full vote buffer 2016-02-28 21:37:06 -05:00
Tux 6a51c1c106 Don't accept > 16 character usernames in v2 2016-02-28 00:09:31 -05:00
Tux 36195be292 Repermit long usernames since some voting sites still insist on sending them
Votifier v2 votes will not accept them, however.
2016-02-28 00:08:26 -05:00
Tux 893fe93ab6 Document hmacEqual() 2016-01-29 21:46:37 -05:00
Tux 90e844b082 formatting tweak 2016-01-29 21:43:03 -05:00
Joseph Hirschfeld 886b60b2de Only send info when in debug mode 2016-01-29 13:40:15 -05:00
Joseph Hirschfeld 30d8e9884c Use 'token' instead of 'key' to avoid confusion between v1 public keys and v2 tokens. 2016-01-29 01:36:03 -05:00
Joseph Hirschfeld 7e05b0752b Add actual link to usage document 2016-01-28 22:38:35 -05:00
Joseph Hirschfeld e124c371b3 Add build notification icon 2016-01-28 22:37:01 -05:00
Joseph Hirschfeld 4af4ae1006 Add additional logging when the votifier port is opened 2016-01-28 22:31:07 -05:00
Tux b63c4a7c68 Make v1 validation a bit smarter in an attempt to accept votes from brain-damaged libraries 2016-01-28 21:26:32 -05:00
33 changed files with 469 additions and 241 deletions

No files matched your search

+1 -1
View File
@@ -6,6 +6,6 @@ deploy:
provider: releases
api_key:
secure: j8y4qeDOXAYjmf6WU6EbpUA6AKTz26yJ4zNb6oAuB4h9Y3PpBRjFjCIBL6jJQnMxZMLjdgk72/3uqS7+//jJ8/iiY/OJhO2L+2us8TJDcQhH5EI1USe/lcZKf60iJjmd9Q9pjh1eHNYhxr6kyNLnpa6fw5b/hIBT6CvNZAJV1qbhRpHwtzbgdlMFaHGnpX192vJNw7E3uuJuSwue5XNK2ybDp81oTrM0Q16JCq/UsQhGq4Ba3JffOhdBhpfg4EI2jJfe3vN8CFqojCYKNSp7eL6DPFPaV4x8Ly8cRIOZ5Zb60m5SyWAcagPKJuUq3ZOTHfeVTjkI9ymZMHe1pla2Mq6eitIBwZIGnonjyMxcTdkR9EJgq9jo+1PzdStR/NlGLakBiwPzk8Ov0DZ+iOwpYUkISUkMU0UgKWwoFoRu18CHtp+ALEsoYZhBQGlcShq+4EGv9VMNBYk9vN3KXNRMVGKqXd6O3T6shvSQgoUn/3mvksOTAuhS5J4U3woHecDrXwl/RfP2aqiHhX/S7p7yY4hVcHu8kiSYpNQMbay+rNd6HxFcRe/U9HOUt6kW8/yo1ROyCxs3ze+N6LtGDHspgzkFaay6I6wMHYNDnzzRP4MC3Zc73zxViXol6dGBk4Tawsm16VaI6b1uyWLBGKvuU23azkmMTHUmGFXl2/BTaYw=
file: universal/target/nuvotifier-universal-2.3.2.jar
file: universal/target/nuvotifier-universal-2.3.4.jar
on:
tags: true
+3 -1
View File
@@ -2,6 +2,8 @@
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.
[![Build Status](https://travis-ci.org/NuVotifier/NuVotifier.svg?branch=master)](https://travis-ci.org/NuVotifier/NuVotifier)
## Why fork Votifier?
Votifier has lagged in activity, has severe security flaws in its protocol, and is vulnerable to denial of service attacks.
@@ -27,7 +29,7 @@ NuVotifier configures itself the first time it is run. If you have an existing V
If you want to customize NuVotifier, simply the edit `./plugins/(Nu)Votifier/config.yml` file.
An in-depth configuration guide is located in `usage.md` of this repository.
An in-depth configuration guide is located in the [usage](https://github.com/NuVotifier/NuVotifier/blob/master/usage.md) document.
### Tokens
+4 -5
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>nuvotifier</artifactId>
<groupId>com.vexsoftware</groupId>
<version>2.3.2</version>
<version>2.3.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -56,10 +56,9 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.7.2-R0.1</version>
<type>jar</type>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -258,7 +258,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
serverChannel = future.channel();
getLogger().info("Votifier enabled.");
getLogger().info("Votifier enabled on socket "+serverChannel.localAddress()+".");
} else {
SocketAddress socketAddress = future.channel().localAddress();
if (socketAddress == null) {
@@ -342,12 +342,12 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
}
@Override
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
getLogger().info("Got a protocol v1 vote record -> " + vote);
getLogger().info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
} else {
getLogger().info("Got a protocol v2 vote record -> " + vote);
getLogger().info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
}
}
Bukkit.getScheduler().runTask(this, new Runnable() {
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>nuvotifier</artifactId>
<groupId>com.vexsoftware</groupId>
<version>2.3.2</version>
<version>2.3.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -221,7 +221,7 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
serverChannel = future.channel();
getLogger().info("Votifier enabled.");
getLogger().info("Votifier enabled on socket "+serverChannel.localAddress()+".");
} else {
SocketAddress socketAddress = future.channel().localAddress();
if (socketAddress == null) {
@@ -293,7 +293,8 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
continue;
}
ProxyForwardingVoteSource.BackendServer server = new ProxyForwardingVoteSource.BackendServer(s,
new InetSocketAddress(address, section.getShort("port")), KeyCreator.createKeyFrom(section.getString("key")));
new InetSocketAddress(address, section.getShort("port")),
KeyCreator.createKeyFrom(section.getString("token",section.getString("key"))));
serverList.add(server);
}
@@ -320,12 +321,12 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
}
@Override
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
getLogger().info("Got a protocol v1 vote record -> " + vote);
getLogger().info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
} else {
getLogger().info("Got a protocol v2 vote record -> " + vote);
getLogger().info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
}
}
@@ -0,0 +1,108 @@
package com.vexsoftware.votifier.bungee.forwarding;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.bungee.forwarding.cache.FileVoteCache;
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.model.Vote;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.event.EventHandler;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public abstract class AbstractPluginMessagingForwardingSource implements ForwardingVoteSource {
public AbstractPluginMessagingForwardingSource(String channel, List<String> ignoredServers, NuVotifier nuVotifier, VoteCache cache) {
ProxyServer.getInstance().registerChannel(channel);
this.channel = channel;
this.nuVotifier = nuVotifier;
this.cache = cache;
this.ignoredServers = ignoredServers;
}
protected AbstractPluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache) {
this(channel, null, nuVotifier, voteCache);
}
protected final NuVotifier nuVotifier;
protected final String channel;
protected final VoteCache cache;
protected final List<String> ignoredServers;
@Override
public void forward(Vote v) {
byte[] rawData = v.serialize().toString().getBytes(StandardCharsets.UTF_8);
for (ServerInfo s : ProxyServer.getInstance().getServers().values()) {
if (ignoredServers != null && ignoredServers.contains(s.getName())) continue;
if (!forwardSpecific(s, rawData)) attemptToAddToCache(v, s.getName());
}
}
protected boolean forwardSpecific(ServerInfo connection, Vote vote) {
byte[] rawData = vote.serialize().toString().getBytes(StandardCharsets.UTF_8);
return connection.sendData(channel, rawData, false);
}
private boolean forwardSpecific(ServerInfo connection, byte[] data) {
return connection.sendData(channel, data, false);
}
@Override
public void halt() {
ProxyServer.getInstance().unregisterChannel(channel);
if (cache != null && cache instanceof FileVoteCache) {
try {
((FileVoteCache) cache).halt();
} catch (IOException e) {
nuVotifier.getLogger().log(Level.SEVERE, "Unable to save cached votes, votes will be lost.", e);
}
}
}
protected void handleServerConnected(final ServerConnectedEvent e) {
if (cache == null) return;
final String serverName = e.getServer().getInfo().getName();
final Collection<Vote> cachedVotes = cache.evict(serverName);
if (!cachedVotes.isEmpty()) {
nuVotifier.getProxy().getScheduler().schedule(nuVotifier, new Runnable() {
@Override
public void run() {
int evicted = 0;
int unsuccessfulEvictions = 0;
for (Vote v : cachedVotes) {
if (forwardSpecific(e.getServer().getInfo(), v)) {
evicted++;
} else {
// Re-add to cache to send later.
cache.addToCache(v, serverName);
unsuccessfulEvictions++;
}
}
if (nuVotifier.isDebug()) {
nuVotifier.getLogger().info("Successfully evicted " + evicted + " votes to server '" + serverName + "'.");
if (unsuccessfulEvictions > 0) {
nuVotifier.getLogger().info("Held " + unsuccessfulEvictions + " votes for server '" + serverName + "'.");
}
}
}
}, 1, TimeUnit.SECONDS);
}
}
protected void attemptToAddToCache(Vote v, String server) {
if (cache != null) {
cache.addToCache(v, server);
if (nuVotifier.isDebug())
nuVotifier.getLogger().info("Added to forwarding cache: " + v + " -> " + server);
} else if (nuVotifier.isDebug())
nuVotifier.getLogger().severe("Could not immediately send vote to backend, vote lost! " + v + " -> " + server);
}
}
@@ -8,16 +8,18 @@ import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
/**
* @author Joseph Hirschfeld
* @date 12/31/2015
*/
public final class OnlineForwardPluginMessagingForwardingSource extends PluginMessagingForwardingSource {
public final class OnlineForwardPluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
public OnlineForwardPluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache cache, String fallbackServer) {
super(channel, nuVotifier, cache);
this.fallbackServer = fallbackServer;
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
}
private final String fallbackServer;
@@ -27,7 +29,7 @@ public final class OnlineForwardPluginMessagingForwardingSource extends PluginMe
ProxiedPlayer p = ProxyServer.getInstance().getPlayer(v.getUsername());
if (p == null || !forwardSpecific(p.getServer().getInfo(), v)) {
ServerInfo serverInfo = ProxyServer.getInstance().getServers().get(fallbackServer);
if (serverInfo == null || !forwardSpecific(serverInfo, v)) {
if (serverInfo != null && !forwardSpecific(serverInfo, v)) {
attemptToAddToCache(v, fallbackServer);
}
}
@@ -1,75 +1,25 @@
package com.vexsoftware.votifier.bungee.forwarding;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.bungee.forwarding.cache.FileVoteCache;
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
import com.vexsoftware.votifier.model.Vote;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
/**
* Created by Joe Hirschfeld on 10/20/2015.
*/
public class PluginMessagingForwardingSource implements ForwardingVoteSource, Listener {
public class PluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
public PluginMessagingForwardingSource(String channel, List<String> ignoredServers, NuVotifier nuVotifier, VoteCache cache) {
ProxyServer.getInstance().registerChannel(channel);
super(channel, ignoredServers, nuVotifier, cache);
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
this.channel = channel;
this.nuVotifier = nuVotifier;
this.cache = cache;
this.ignoredServers = ignoredServers;
}
protected PluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache) {
this(channel, null, nuVotifier, voteCache);
}
protected final NuVotifier nuVotifier;
protected final String channel;
protected final VoteCache cache;
protected final List<String> ignoredServers;
@Override
public void forward(Vote v) {
byte[] rawData = v.serialize().toString().getBytes(StandardCharsets.UTF_8);
for (ServerInfo s : ProxyServer.getInstance().getServers().values()) {
if (ignoredServers != null && ignoredServers.contains(s.getName())) continue;
if (!forwardSpecific(s, rawData)) attemptToAddToCache(v, s.getName());
}
}
protected boolean forwardSpecific(ServerInfo connection, Vote vote) {
byte[] rawData = vote.serialize().toString().getBytes(StandardCharsets.UTF_8);
return connection.sendData(channel, rawData, false);
}
private boolean forwardSpecific(ServerInfo connection, byte[] data) {
return connection.sendData(channel, data, false);
}
@Override
public void halt() {
ProxyServer.getInstance().unregisterChannel(channel);
if (cache != null && cache instanceof FileVoteCache) {
try {
((FileVoteCache) cache).halt();
} catch (IOException e) {
nuVotifier.getLogger().log(Level.SEVERE, "Unable to save cached votes, votes will be lost.", e);
}
}
super(channel, nuVotifier, voteCache);
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
}
@EventHandler
@@ -81,43 +31,4 @@ public class PluginMessagingForwardingSource implements ForwardingVoteSource, Li
public void onServerConnected(final ServerConnectedEvent e) { //Attempt to resend any votes that were previously cached.
handleServerConnected(e);
}
protected void handleServerConnected(final ServerConnectedEvent e) {
if (cache == null) return;
final String serverName = e.getServer().getInfo().getName();
final Collection<Vote> cachedVotes = cache.evict(serverName);
if (!cachedVotes.isEmpty()) {
nuVotifier.getProxy().getScheduler().schedule(nuVotifier, new Runnable() {
@Override
public void run() {
int evicted = 0;
int unsuccessfulEvictions = 0;
for (Vote v : cachedVotes) {
if (forwardSpecific(e.getServer().getInfo(), v)) {
evicted++;
} else {
// Re-add to cache to send later.
cache.addToCache(v, serverName);
unsuccessfulEvictions++;
}
}
if (nuVotifier.isDebug()) {
nuVotifier.getLogger().info("Successfully evicted " + evicted + " votes to server '" + serverName + "'.");
if (unsuccessfulEvictions > 0) {
nuVotifier.getLogger().info("Held " + unsuccessfulEvictions + " votes for server '" + serverName + "'.");
}
}
}
}, 1, TimeUnit.SECONDS);
}
}
protected void attemptToAddToCache(Vote v, String server) {
if (cache != null) {
cache.addToCache(v, server);
if (nuVotifier.isDebug())
nuVotifier.getLogger().info("Added to forwarding cache: " + v + " -> " + server);
} else if (nuVotifier.isDebug())
nuVotifier.getLogger().severe("Could not immediately send vote to backend, vote lost! " + v + " -> " + server);
}
}
@@ -7,15 +7,11 @@ import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -42,37 +38,41 @@ public class FileVoteCache extends MemoryVoteCache {
}
private void load() throws IOException {
if (cacheFile.exists()) {
// Load the cache from disk
JSONObject object = new JSONObject(Files.toString(cacheFile, StandardCharsets.UTF_8));
// Load the cache from disk
JSONObject object;
try (BufferedReader reader = Files.newReader(cacheFile, StandardCharsets.UTF_8)) {
object = new JSONObject(new JSONTokener(reader));
} catch (FileNotFoundException e) {
object = new JSONObject();
}
// Deserialize all votes contained
for (Object server : object.keySet()) {
JSONArray voteArray = object.optJSONArray(((String) server));
if (voteArray == null) continue;
Set<Vote> votes = new LinkedHashSet<>(voteArray.length());
for (int i = 0; i < voteArray.length(); i++) {
votes.add(new Vote(voteArray.getJSONObject(i)));
}
voteCache.put(((String) server), votes);
// Deserialize all votes contained
for (Object server : object.keySet()) {
JSONArray voteArray = object.getJSONArray(((String) server));
List<Vote> votes = new ArrayList<>(voteArray.length());
for (int i = 0; i < voteArray.length(); i++) {
JSONObject voteObject = voteArray.getJSONObject(i);
votes.add(new Vote(voteObject));
}
} else {
cacheFile.createNewFile();
voteCache.put(((String) server), votes);
}
}
public void save() throws IOException {
cacheLock.lock();
// Create a copy of the votes.
JSONObject votesObject = new JSONObject();
for (Map.Entry<String, Collection<Vote>> entry : voteCache.entrySet()) {
JSONArray array = new JSONArray();
for (Vote vote : entry.getValue()) {
array.put(vote.serialize());
try {
// Create a copy of the votes.
for (Map.Entry<String, Collection<Vote>> entry : voteCache.entrySet()) {
JSONArray array = new JSONArray();
for (Vote vote : entry.getValue()) {
array.put(vote.serialize());
}
votesObject.put(entry.getKey(), array);
}
votesObject.put(entry.getKey(), array);
} finally {
cacheLock.unlock();
}
cacheLock.unlock();
try (BufferedWriter writer = Files.newWriter(cacheFile, StandardCharsets.UTF_8)) {
votesObject.write(writer);
@@ -1,5 +1,6 @@
package com.vexsoftware.votifier.bungee.forwarding.cache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.vexsoftware.votifier.model.Vote;
@@ -22,28 +23,38 @@ public class MemoryVoteCache implements VoteCache {
@Override
public Collection<String> getCachedServers() {
cacheLock.lock();
Set<String> servers = ImmutableSet.copyOf(voteCache.keySet());
cacheLock.unlock();
return servers;
try {
return ImmutableList.copyOf(voteCache.keySet());
} finally {
cacheLock.unlock();
}
}
@Override
public void addToCache(Vote v, String server) {
if (server == null) throw new NullPointerException();
cacheLock.lock();
Collection<Vote> voteCollection = voteCache.get(server);
if (voteCollection == null) {
voteCollection = new LinkedHashSet<>();
voteCache.put(server, voteCollection);
try {
Collection<Vote> voteCollection = voteCache.get(server);
if (voteCollection == null) {
voteCollection = new ArrayList<>();
voteCache.put(server, voteCollection);
}
voteCollection.add(v);
} finally {
cacheLock.unlock();
}
voteCollection.add(v);
cacheLock.unlock();
}
@Override
public Collection<Vote> evict(String server) {
if (server == null) throw new NullPointerException();
cacheLock.lock();
Collection<Vote> fromCollection = voteCache.remove(server);
cacheLock.unlock();
return fromCollection != null ? ImmutableSet.copyOf(fromCollection) : ImmutableSet.<Vote>of();
try {
Collection<Vote> fromCollection = voteCache.remove(server);
return fromCollection != null ? fromCollection : ImmutableList.<Vote>of();
} finally {
cacheLock.unlock();
}
}
}
@@ -76,7 +76,7 @@ public class ProxyForwardingVoteSource implements ForwardingVoteSource {
public void onFailure(Throwable error) {
handleFailure(server, v, error, tries);
}
}));
}, plugin));
}
})
.connect(server.address)
@@ -1,5 +1,6 @@
package com.vexsoftware.votifier.bungee.forwarding.proxy.client;
import com.vexsoftware.votifier.bungee.NuVotifier;
import com.vexsoftware.votifier.model.Vote;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@@ -8,10 +9,12 @@ import io.netty.handler.codec.CorruptedFrameException;
public class VotifierProtocol2HandshakeHandler extends SimpleChannelInboundHandler<String> {
private final Vote toSend;
private final VotifierResponseHandler responseHandler;
private final NuVotifier nuVotifier;
public VotifierProtocol2HandshakeHandler(Vote toSend, VotifierResponseHandler responseHandler) {
public VotifierProtocol2HandshakeHandler(Vote toSend, VotifierResponseHandler responseHandler, NuVotifier nuVotifier) {
this.toSend = toSend;
this.responseHandler = responseHandler;
this.nuVotifier = nuVotifier;
}
@Override
@@ -22,7 +25,9 @@ public class VotifierProtocol2HandshakeHandler extends SimpleChannelInboundHandl
}
VoteRequest request = new VoteRequest(handshakeContents[2], toSend);
System.out.println(request);
if (nuVotifier.isDebug()) {
nuVotifier.getLogger().info("Sent request: " + request.toString());
}
ctx.writeAndFlush(request);
ctx.pipeline().addLast(new VotifierProtocol2ResponseHandler(responseHandler));
ctx.pipeline().remove(this);
@@ -58,8 +58,8 @@ forwarding:
Hub:
address: 127.0.0.1
port: 8193
key: MyKeyHere
token: MyKeyHere
Hub2:
address: 127.0.0.1
port: 8194
key: AnotherKeyHere
token: AnotherKeyHere
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>nuvotifier</artifactId>
<groupId>com.vexsoftware</groupId>
<version>2.3.2</version>
<version>2.3.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -5,7 +5,7 @@ import com.vexsoftware.votifier.net.VotifierSession;
import io.netty.channel.Channel;
public interface VoteHandler {
void onVoteReceived(Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception;
void onVoteReceived(Channel channel, Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception;
void onError(Channel channel, Throwable throwable);
}
@@ -52,9 +52,6 @@ public class Vote {
}
public Vote(String serviceName, String username, String address, String timeStamp) {
if (username.length() > 16) {
throw new IllegalArgumentException("Username is too long.");
}
this.serviceName = serviceName;
this.username = username;
this.address = address;
@@ -105,9 +102,6 @@ public class Vote {
*/
@Deprecated
public void setUsername(String username) {
if (username.length() > 16) {
throw new IllegalArgumentException("Username is too long.");
}
this.username = username;
}
@@ -19,7 +19,7 @@ public class VoteInboundHandler extends SimpleChannelInboundHandler<Vote> {
protected void channelRead0(ChannelHandlerContext ctx, final Vote vote) throws Exception {
VotifierSession session = ctx.channel().attr(VotifierSession.KEY).get();
handler.onVoteReceived(vote, session.getVersion());
handler.onVoteReceived(ctx.channel(), vote, session.getVersion());
if (session.getVersion() == VotifierSession.ProtocolVersion.ONE) {
ctx.close();
@@ -17,10 +17,14 @@ import java.util.List;
public class VotifierProtocol1Decoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
byte[] block = new byte[256];
if (buf.readableBytes() < 256) {
return;
}
byte[] block = new byte[buf.readableBytes()];
buf.getBytes(0, block);
// "Drain" the whole buffer
buf.readerIndex(buf.capacity());
buf.readerIndex(buf.readableBytes());
VotifierPlugin plugin = ctx.channel().attr(VotifierPlugin.KEY).get();
@@ -12,10 +12,7 @@ import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.*;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@@ -63,6 +60,10 @@ public class VotifierProtocol2Decoder extends MessageToMessageDecoder<String> {
UUID.fromString(votePayload.getString("uuid"));
}
if (votePayload.getString("username").length() > 16) {
throw new CorruptedFrameException("Username too long");
}
// Create the vote.
Vote vote = new Vote(votePayload);
list.add(vote);
@@ -75,16 +76,19 @@ public class VotifierProtocol2Decoder extends MessageToMessageDecoder<String> {
// This randomizes the byte order to make timing attacks more difficult.
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
byte[] messageCalc = mac.doFinal(message);
byte[] calculatedSig = mac.doFinal(message);
Mac mac2 = Mac.getInstance("HmacSHA256");
// Generate a random key for use in comparison
byte[] randomKey = new byte[32];
RANDOM.nextBytes(randomKey);
// Then generate two HMACs for the different signatures found
Mac mac2 = Mac.getInstance("HmacSHA256");
mac2.init(new SecretKeySpec(randomKey, "HmacSHA256"));
byte[] clientSig = mac2.doFinal(sig);
mac2.reset();
byte[] realSig = mac2.doFinal(messageCalc);
byte[] realSig = mac2.doFinal(calculatedSig);
return Arrays.equals(clientSig, realSig);
return MessageDigest.isEqual(clientSig, realSig);
}
}
@@ -29,12 +29,10 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
// Determine the number of bytes that are available.
int readable = buf.readableBytes();
buf.retain(); // TODO: Is this needed?
if (readable < 2) {
// Some retarded voting sites (PMC?) seem to send empty buffers for no good reason.
// TODO: How can we handle this?
throw new CorruptedFrameException("Frame is too short");
return;
}
short readMagic = buf.readShort();
@@ -46,6 +44,8 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
if (readMagic == PROTOCOL_2_MAGIC) {
// Short 0x733A + Message = Protocol v2 Vote
session.setVersion(VotifierSession.ProtocolVersion.TWO);
if (!testMode) {
ctx.pipeline().addAfter("protocolDifferentiator", "protocol2LengthDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, 0, 4));
ctx.pipeline().addAfter("protocol2LengthDecoder", "protocol2StringDecoder", new StringDecoder(StandardCharsets.UTF_8));
@@ -53,17 +53,16 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
ctx.pipeline().addAfter("protocol2VoteDecoder", "protocol2StringEncoder", new StringEncoder(StandardCharsets.UTF_8));
ctx.pipeline().remove(this);
}
session.setVersion(VotifierSession.ProtocolVersion.TWO);
} else {
if (!allowv1) {
throw new CorruptedFrameException("This server only accepts well-formed Votifier v2 packets.");
}
// Probably Protocol v1 Vote Message
session.setVersion(VotifierSession.ProtocolVersion.ONE);
if (!testMode) {
ctx.pipeline().addAfter("protocolDifferentiator", "protocol1Handler", new VotifierProtocol1Decoder());
ctx.pipeline().remove(this);
}
session.setVersion(VotifierSession.ProtocolVersion.ONE);
}
}
}
@@ -21,6 +21,7 @@ package com.vexsoftware.votifier.net.protocol.v1crypto;
import javax.xml.bind.DatatypeConverter;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.KeyPair;
@@ -45,21 +46,18 @@ public class RSAIO {
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// Store the public key.
// Store the public and private keys.
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(
publicKey.getEncoded());
FileOutputStream out = new FileOutputStream(directory + "/public.key");
out.write(DatatypeConverter.printBase64Binary(publicSpec.getEncoded())
.getBytes());
out.close();
// Store the private key.
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(
privateKey.getEncoded());
out = new FileOutputStream(directory + "/private.key");
out.write(DatatypeConverter.printBase64Binary(privateSpec.getEncoded())
.getBytes());
out.close();
try (FileOutputStream publicOut = new FileOutputStream(directory + "/public.key");
FileOutputStream privateOut = new FileOutputStream(directory + "/private.key")) {
publicOut.write(DatatypeConverter.printBase64Binary(publicSpec.getEncoded())
.getBytes(StandardCharsets.UTF_8));
privateOut.write(DatatypeConverter.printBase64Binary(privateSpec.getEncoded())
.getBytes(StandardCharsets.UTF_8));
}
}
/**
@@ -75,13 +73,13 @@ public class RSAIO {
File publicKeyFile = new File(directory + "/public.key");
byte[] encodedPublicKey = Files.readAllBytes(publicKeyFile.toPath());
encodedPublicKey = DatatypeConverter.parseBase64Binary(new String(
encodedPublicKey));
encodedPublicKey, StandardCharsets.UTF_8));
// Read the private key file.
File privateKeyFile = new File(directory + "/private.key");
byte[] encodedPrivateKey = Files.readAllBytes(privateKeyFile.toPath());
encodedPrivateKey = DatatypeConverter.parseBase64Binary(new String(
encodedPrivateKey));
encodedPrivateKey, StandardCharsets.UTF_8));
// Instantiate and return the key pair.
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
@@ -0,0 +1,89 @@
package com.vexsoftware.votifier.util.standalone;
import com.vexsoftware.votifier.VoteHandler;
import com.vexsoftware.votifier.VotifierPlugin;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import com.vexsoftware.votifier.net.protocol.VoteInboundHandler;
import com.vexsoftware.votifier.net.protocol.VotifierGreetingHandler;
import com.vexsoftware.votifier.net.protocol.VotifierProtocolDifferentiator;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
import java.security.Key;
import java.security.KeyPair;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class StandaloneVotifierPlugin implements VotifierPlugin, VoteHandler {
private final Map<String, Key> tokens;
private final VoteReceiver receiver;
private final KeyPair v1Key;
private final InetSocketAddress bind;
private NioEventLoopGroup group;
public StandaloneVotifierPlugin(Map<String, Key> tokens, VoteReceiver receiver, KeyPair v1Key, InetSocketAddress bind) {
this.receiver = receiver;
this.bind = bind;
this.tokens = Collections.unmodifiableMap(new HashMap<>(tokens));
this.v1Key = v1Key;
}
public Throwable start() throws Throwable {
group = new NioEventLoopGroup(2);
ChannelFuture future = new ServerBootstrap()
.channel(NioServerSocketChannel.class)
.group(group)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) throws Exception {
channel.attr(VotifierSession.KEY).set(new VotifierSession());
channel.attr(VotifierPlugin.KEY).set(StandaloneVotifierPlugin.this);
channel.pipeline().addLast("greetingHandler", new VotifierGreetingHandler());
channel.pipeline().addLast("protocolDifferentiator", new VotifierProtocolDifferentiator(false, v1Key != null));
channel.pipeline().addLast("voteHandler", new VoteInboundHandler(StandaloneVotifierPlugin.this));
}
})
.bind(bind)
.syncUninterruptibly();
if (!future.isSuccess()) {
return future.cause();
} else {
return null;
}
}
@Override
public Map<String, Key> getTokens() {
return tokens;
}
@Override
public KeyPair getProtocolV1Key() {
return v1Key;
}
@Override
public String getVersion() {
return "2.3.4";
}
@Override
public void onVoteReceived(Channel channel, Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
receiver.onVote(vote);
}
@Override
public void onError(Channel channel, Throwable throwable) {
receiver.onException(throwable);
}
}
@@ -0,0 +1,8 @@
package com.vexsoftware.votifier.util.standalone;
import com.vexsoftware.votifier.model.Vote;
public interface VoteReceiver {
void onVote(Vote vote) throws Exception;
void onException(Throwable throwable);
}
@@ -0,0 +1,57 @@
package com.vexsoftware.votifier.util.standalone;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAIO;
import com.vexsoftware.votifier.util.KeyCreator;
import java.io.File;
import java.net.InetSocketAddress;
import java.security.Key;
import java.security.KeyPair;
import java.security.interfaces.RSAKey;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class VotifierServerBuilder {
private final Map<String, Key> keyMap = new HashMap<>();
private KeyPair v1Key;
private InetSocketAddress bind;
private VoteReceiver receiver;
public VotifierServerBuilder addToken(String service, String token) {
Objects.requireNonNull(service, "service");
Objects.requireNonNull(token, "key");
keyMap.put(service, KeyCreator.createKeyFrom(token));
return this;
}
public VotifierServerBuilder v1Key(KeyPair v1Key) {
this.v1Key = Objects.requireNonNull(v1Key, "v1Key");
if (!(v1Key.getPrivate() instanceof RSAKey)) {
throw new IllegalArgumentException("Provided key is not an RSA key.");
}
return this;
}
public VotifierServerBuilder v1KeyFolder(File file) throws Exception {
this.v1Key = RSAIO.load(Objects.requireNonNull(file, "file"));
return this;
}
public VotifierServerBuilder bind(InetSocketAddress bind) {
this.bind = Objects.requireNonNull(bind, "bind");
return this;
}
public VotifierServerBuilder receiver(VoteReceiver receiver) {
this.receiver = Objects.requireNonNull(receiver, "receiver");
return this;
}
public StandaloneVotifierPlugin create() {
Objects.requireNonNull(bind, "need an address to bind to");
Objects.requireNonNull(receiver, "need a receiver for votes");
return new StandaloneVotifierPlugin(keyMap, receiver, v1Key, bind);
}
}
@@ -0,0 +1,31 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.protocol.v1crypto.RSA;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
public class VoteUtil {
public static byte[] encodePOJOv1(Vote vote) throws Exception {
return encodePOJOv1(vote, TestVotifierPlugin.getI().getProtocolV1Key().getPublic());
}
public static byte[] encodePOJOv1(Vote vote, PublicKey key) throws Exception {
List<String> ordered = new ArrayList<>();
ordered.add("VOTE");
ordered.add(vote.getServiceName());
ordered.add(vote.getUsername());
ordered.add(vote.getAddress());
ordered.add(vote.getTimeStamp());
StringBuilder builder = new StringBuilder();
for (String s : ordered) {
builder.append(s).append('\n'); // naive join needed!
}
return RSA.encrypt(builder.toString().getBytes(StandardCharsets.UTF_8), key);
}
}
@@ -29,16 +29,12 @@ public class VotifierProtocol1DecoderTest {
@Test
public void testSuccessfulDecode() throws Exception {
// Encode a test vote.
String voteString = "VOTE\nTest\ntest\ntest\ntest\n";
// For reference, this is the same vote as a POJO:
Vote votePojo = new Vote("Test", "test", "test", "test");
// Send the vote
EmbeddedChannel channel = createChannel();
byte[] encrypted = RSA.encrypt(voteString.getBytes(StandardCharsets.UTF_8), TestVotifierPlugin.getI().getProtocolV1Key().getPublic());
byte[] encrypted = VoteUtil.encodePOJOv1(votePojo);
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
assertTrue(channel.writeInbound(encryptedByteBuf));
@@ -81,8 +77,7 @@ public class VotifierProtocol1DecoderTest {
// Send the bad vote
EmbeddedChannel channel = createChannel();
String voteString = "VOTE\nTest\ntest\ntest\ntest\n";
byte[] encrypted = RSA.encrypt(voteString.getBytes(StandardCharsets.UTF_8), badPublicKey);
byte[] encrypted = VoteUtil.encodePOJOv1(new Vote("Test", "test", "test", "test"), badPublicKey);
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
try {
@@ -1,5 +1,6 @@
package com.vexsoftware.votifier.net.protocol;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.net.VotifierSession;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
@@ -62,21 +63,6 @@ public class VotifierProtocolDifferentiatorTest {
channel.close();
}
@Test(expected = DecoderException.class)
public void failOnSmallBufferTest() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
ByteBuf buf = Unpooled.buffer(1);
buf.writeByte(0);
try {
channel.writeInbound(buf);
} finally {
buf.release();
channel.close();
}
}
@Test(expected = DecoderException.class)
public void failOnBadPacketTest() {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
@@ -93,4 +79,22 @@ public class VotifierProtocolDifferentiatorTest {
channel.close();
}
}
@Test
public void tryIdentifyRealVotev1() throws Exception {
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, true));
VotifierSession session = new VotifierSession();
channel.attr(VotifierSession.KEY).set(session);
Vote votePojo = new Vote("Test", "test", "test", "test");
byte[] encrypted = VoteUtil.encodePOJOv1(votePojo);
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
channel.writeInbound(encryptedByteBuf);
assertEquals(VotifierSession.ProtocolVersion.ONE, session.getVersion());
encryptedByteBuf.release();
channel.close();
}
}
+3 -3
View File
@@ -4,7 +4,7 @@
<groupId>com.vexsoftware</groupId>
<artifactId>nuvotifier</artifactId>
<packaging>pom</packaging>
<version>2.3.2</version>
<version>2.3.4</version>
<modules>
<module>common</module>
<module>bukkit</module>
@@ -23,8 +23,8 @@
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
<repository>
<id>bukkit</id>
<url>http://repo.bukkit.org/content/groups/public/</url>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
+7 -2
View File
@@ -5,12 +5,17 @@
<parent>
<artifactId>nuvotifier</artifactId>
<groupId>com.vexsoftware</groupId>
<version>2.3.2</version>
<version>2.3.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nuvotifier-sponge</artifactId>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>sponge-maven-repo</id>
@@ -50,7 +55,7 @@
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>3.0.0</version>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -34,6 +34,7 @@ import org.spongepowered.api.Sponge;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.event.cause.NamedCause;
import org.spongepowered.api.event.game.state.GameStartedServerEvent;
import org.spongepowered.api.event.game.state.GameStoppingServerEvent;
import org.spongepowered.api.plugin.Plugin;
@@ -52,7 +53,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@Plugin(id = "nuvotifier", name = "NuVotifier", version = "2.3.2")
@Plugin(id = "nuvotifier", name = "NuVotifier", version = "2.3.4")
public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.VotifierPlugin, ForwardedVoteListener {
private Logger logger;
@Inject
@@ -229,7 +230,7 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.Vot
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
serverChannel = future.channel();
logger.info("Votifier enabled.");
logger.info("Votifier enabled on socket "+serverChannel.localAddress()+".");
} else {
SocketAddress socketAddress = future.channel().localAddress();
if (socketAddress == null) {
@@ -339,19 +340,19 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.Vot
}
@Override
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
if (debug) {
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
logger.info("Got a protocol v1 vote record -> " + vote);
logger.info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
} else {
logger.info("Got a protocol v2 vote record -> " + vote);
logger.info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
}
}
Sponge.getScheduler().createTaskBuilder()
.execute(new Runnable() {
@Override
public void run() {
VotifierEvent event = new VotifierEvent(vote, Cause.of(VotifierPlugin.this));
VotifierEvent event = new VotifierEvent(vote, Cause.of(NamedCause.of("Vote", vote)));
Sponge.getEventManager().post(event);
}
})
@@ -377,7 +378,7 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.Vot
.execute(new Runnable() {
@Override
public void run() {
VotifierEvent event = new VotifierEvent(v, Cause.of(VotifierPlugin.this));
VotifierEvent event = new VotifierEvent(v, Cause.of(NamedCause.of("ForwardedVote", v)));
Sponge.getEventManager().post(event);
}
})
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>nuvotifier</artifactId>
<groupId>com.vexsoftware</groupId>
<version>2.3.2</version>
<version>2.3.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+12 -12
View File
@@ -4,7 +4,7 @@ networks.
# Installation
The NuVotifier Universal jar (the default jar that is distributed on spigotmc.org) can be installed on Bukkit, BungeeCord,
and Sponge servers as-is without modification.
and Sponge servers as-is.
The Universal JAR can be downloaded off our [SpigotMC resource page](https://www.spigotmc.org/resources/nuvotifier.13449/).
@@ -18,7 +18,7 @@ the legacy protocol will be referred to v1. As of writing, most if not all vote
## Enabling the NuVotifier Server port
NuVotifier allows for the server port to be turned off when NuVotifier is only being used to forward votes. This is only
NuVotifier allows for the server port to be turned off when NuVotifier is only receiving forwarded votes. This is only
an option on Bukkit based servers, since it would not make logical sense for the port to be turned off on Bungee servers.
If you are receiving votes from a server list directly on the server in question (votes are not being forwarded through plugin
@@ -28,10 +28,10 @@ be set to false.
## Port Configuration
One of NuVotifier's functions is acting as a vote server (receiver). The IP for NuVotifier to bind to is configured
NuVotifier's main function is acting as a vote server (receiver). The IP for NuVotifier to bind to is configured
by the `host` and `ip` configuration values within the config.yml files within the NuVotifier plugin folder.
Most times, you will want this `host` value to be set to `0.0.0.0` or your server's ip. `0.0.0.0` will bind to all
ip ports on your machine, while specifying a certain IP will only bind to that ip. If you don't know what to set this
network addresses on your machine, while specifying a certain IP will only bind to that ip. If you don't know what to set this
to, `0.0.0.0` is a safe bet. By default, NuVotifier listens on port 8192. This is the same as the default Votifier port.
If you change this, you will have to remember this when setting up forwarding or server lists.
@@ -39,7 +39,7 @@ If you change this, you will have to remember this when setting up forwarding or
When first setting up NuVotifier, you will probably want debug mode to be enabled. This prints extra information to the
console, ensuring that votes are being processed as they should. If you are experiencing an error, or having issues with
votes being processed, you should first look to enable this. Debug mode can be turned on by setting `debug` in the config.yml
votes being processed, you should first enable this. Debug mode can be turned on by setting `debug` in the config.yml
to true.
## Tokens vs Public Key
@@ -51,7 +51,7 @@ should be used. When setting up the `proxy` forwarding method, tokens should be
Making a new token is as easy as adding another entry into `tokens` with the serviceName as the key and whatever string you want
as the value. You can usually find the serviceName of a vote provider on their setup page if they use v2.
Unless a website specifies it, you should assume that the website is using v1, and you should enter your `rsa/public.key`.
Unless a website specifies it, you should assume that the website is using v1, and you should use `rsa/public.key`.
## Forwarding
@@ -90,22 +90,22 @@ This is suitable for testing, but not for production system.
Votes can also be cached in a file located in the NuVotifier plugin folder. To enable this, you can set `cache` to file.
If you want to change the file location from the default, you can set this under the `name` config under the `file` section.
This caching method is suitable for most people.
This caching method is suitable for most people. This is the default option.
##### No Caching
Caching can also be turned off. This is the default option. You can do this by setting `cache` to `none`. This is also not
recommended, and is only suitable for a certain number of setups.
Caching can also be turned off. You can do this by setting `cache` to `none`. This is also not recommended, and is only
suitable for a certain number of setups.
### Proxy Based Forwarding
You can also forward votes using the proxy method. This will cause the BungeeCord to act as a voting websites, sending votes
You can also forward votes using the proxy method. This will cause the BungeeCord to act like a voting website, sending votes
to each server's NuVotifier port. To enable this method, you must set the `method` under `forwarding` to `proxy` in your
BungeeCord config.yml. Within the server's config.yml, `forwarding` should be set to `none`.
For each server you wish to forward to, you must add each to your BungeeCord config.yml under the `proxy` section of `forwarding`.
For each, you must specify the ip address (`address`), port (`port`) and token (`key`). Each backend Bukkit server should have
1 default token under the `tokens` section. This is the token that should be used as the token for the `key` in that server's
For each, you must specify the ip address (`address`), port (`port`) and token (`token`). Each backend Bukkit server should have
1 default token under the `tokens` section. This is the token that should be used as the token for the `token` in that server's
section within the `proxy` section. If the server is on the same server (same ip) as the BungeeCord server, you may
use the `127.0.0.1` or `localhost` address, setting both the `proxy` forwarding address and Bukkit config.yml address to it.