Compare commits

...
36 Commits
Author SHA1 Message Date
BenCodez 17574f6c50 Bump version number to 6.19.1 2025-11-09 18:46:27 -06:00
BenCodez 60377deb44 Improve configuration comments for MySQL settings 2025-11-09 16:00:35 -06:00
Ben 34614e13fa Merge pull request #1469 from BoredManCodes/patch-1
Removed color codes from DiscordSRV messages
2025-11-03 16:17:07 -06:00
Trent Buckley bc98630388 Removed color codes from config yml 2025-11-04 09:12:04 +11:00
Trent Buckley 302cc980d8 Remove color codes from DiscordSRV messages 2025-11-04 09:10:46 +11:00
BenCodez 4342fc5458 Implement monthly reset for milestone data in proxy
Should reduce rare cases with counts being out of sync
2025-11-01 19:14:19 -06:00
BenCodez 90b981af7b Improve VoteCache null checks 2025-10-28 18:43:37 -06:00
BenCodez 32879956ec Refactor PlayerVoteListener to resolve Bedrock player names 2025-10-18 19:57:57 -06:00
Ben 2b166c7760 Update Maven CI workflow with permissions and comments 2025-10-13 08:10:14 -06:00
Ben 9848172a49 Update Maven workflow for JDK setup and build 2025-10-12 20:53:22 -06:00
BenCodez 9775d06ba9 Bump version number to 1.21.10-R0.1-SNAPSHOT 2025-10-12 20:51:10 -06:00
BenCodez 9584bc0e66 Prevent broadcasting for hidden vote sites 2025-10-10 21:05:25 -06:00
Ben 00ec202140 Merge pull request #1465 from BenCodez/dependabot/maven/VotingPlugin/org.apache.maven.plugins-maven-javadoc-plugin-3.12.0
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.3 to 3.12.0 in /VotingPlugin
2025-10-04 20:45:47 -06:00
Ben bd57f003dd Merge pull request #1463 from BenCodez/dependabot/maven/VotingPlugin/org.apache.maven.plugins-maven-shade-plugin-3.6.1
Bump org.apache.maven.plugins:maven-shade-plugin from 3.6.0 to 3.6.1 in /VotingPlugin
2025-10-04 20:45:39 -06:00
Ben 23788be684 Merge pull request #1462 from BenCodez/dependabot/maven/VotingPlugin/com.discordsrv-discordsrv-1.30.1
Bump com.discordsrv:discordsrv from 1.30.0 to 1.30.1 in /VotingPlugin
2025-10-04 20:45:27 -06:00
BenCodez 9b33b1cde7 Bump version numbers to 1.21.9-R0.1-SNAPSHOT and 1.21-R0.4 2025-10-04 20:32:53 -06:00
BenCodez 02fb353dac Fixed proxy votecache not using existing connection unless debug is on 2025-09-28 13:42:20 -06:00
dependabot[bot] a4c470887f Bump org.apache.maven.plugins:maven-javadoc-plugin in /VotingPlugin
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.3 to 3.12.0.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.3...maven-javadoc-plugin-3.12.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
  dependency-version: 3.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 06:09:54 +00:00
dependabot[bot] e844a1eb63 Bump org.apache.maven.plugins:maven-shade-plugin in /VotingPlugin
Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.6.0 to 3.6.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.6.0...v3.6.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-version: 3.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 06:38:19 +00:00
dependabot[bot] 1787595ea2 Bump com.discordsrv:discordsrv from 1.30.0 to 1.30.1 in /VotingPlugin
Bumps com.discordsrv:discordsrv from 1.30.0 to 1.30.1.

---
updated-dependencies:
- dependency-name: com.discordsrv:discordsrv
  dependency-version: 1.30.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 06:28:59 +00:00
BenCodez 79bea20039 Refactor vote caching: optimize cache handling 2025-09-14 14:37:53 -06:00
BenCodez 63905004b6 Remove commented-out code in VotingPluginProxyVoteCacheTest 2025-09-14 13:25:04 -06:00
BenCodez f326e2cbbb Refactor vote caching: streamline methods and improve performance 2025-09-11 19:04:17 -06:00
BenCodez 9f6edca531 Refactor proxy vote caching: remove VoteCache class 2025-09-11 17:45:16 -06:00
BenCodez 3e277a4a76 Update pom.xml to disable minimizeJar and add resource transformers 2025-09-06 17:58:06 -06:00
BenCodez 3b33e5e4a7 Adjust time calculation in PlaceHolders to account for timeWeekOffSet 2025-09-06 09:54:34 -06:00
BenCodez b5f35038fa Add support for timeWeekOffSet in Bungee and Velocity configurations 2025-09-06 09:52:02 -06:00
BenCodez b937d30213 Update shadedPattern in pom.xml to use simpleapi for Hikari 2025-09-05 15:52:58 -06:00
BenCodez d43cf53276 Remove unused documentation comments from vote cache table classes 2025-09-02 20:46:24 -06:00
BenCodez 6a7b677322 Fix casing for MySQL configuration in vote cache settings 2025-09-02 20:28:02 -06:00
BenCodez b309537f97 Add support for using mysql to handle proxy vote cache
Requires more testing
2025-09-02 20:25:43 -06:00
BenCodez 7e34dd89b8 Refactor player name retrieval to use proper casing from user manager 2025-08-31 14:30:31 -06:00
BenCodez 5dcc0a1b33 Improve player name retrieval to match casing from UUID cache 2025-08-31 14:04:12 -06:00
BenCodez 36f8b3fbe8 Update version number to 6.19.1-SNAPSHOT 2025-08-24 16:03:33 -06:00
BenCodez df298fd66c Temp revert version number for maven deploy 2025-08-24 16:00:05 -06:00
BenCodez c93aff6c20 Update version number to 6.19.1-SNAPSHOT 2025-08-24 15:57:21 -06:00
27 changed files with 2103 additions and 517 deletions

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support documentation.
name: Java CI with Maven
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: write # required for dependency submission
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B -f VotingPlugin/pom.xml package
# Only submit dependency snapshot on push (not on pull_request)
- name: Submit Dependency Snapshot
if: github.event_name == 'push'
uses: advanced-security/maven-dependency-submission-action@v5
with:
directory: VotingPlugin # path to pom.xml
# optional: maven-args: "-DskipTests"
+18 -13
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.bencodez</groupId>
<artifactId>votingplugin</artifactId>
<version>6.19</version>
<version>6.19.1</version>
<packaging>jar</packaging>
<name>VotingPlugin</name>
<properties>
@@ -84,10 +84,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<version>3.6.1</version>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<minimizeJar>true</minimizeJar>
<minimizeJar>false</minimizeJar>
<filters>
<filter>
<artifact>mysql-connector-java</artifact>
@@ -131,8 +131,7 @@
</relocation>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>
${project.groupId}.votingplugin.hikari</shadedPattern>
<shadedPattern>${project.groupId}.votingplugin.simpleapi.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
@@ -161,7 +160,8 @@
<excludes>
<exclude>com.google.*:*</exclude>
</excludes>
</artifactSet>
</artifactSet>
<minimizeJar>false</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
@@ -170,9 +170,14 @@
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>module-info.class</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
@@ -218,7 +223,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.8-R0.1-SNAPSHOT</version>
<version>1.21.10-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -230,7 +235,7 @@
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.21-R0.3</version>
<version>1.21-R0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -266,7 +271,7 @@
<dependency>
<groupId>com.bencodez</groupId>
<artifactId>advancedcore</artifactId>
<version>3.7.18</version>
<version>3.7.19</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -284,7 +289,7 @@
<dependency>
<groupId>com.discordsrv</groupId>
<artifactId>discordsrv</artifactId>
<version>1.30.0</version>
<version>1.30.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -369,7 +374,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.3</version>
<version>3.12.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
@@ -444,7 +449,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.3</version>
<version>3.12.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
@@ -586,7 +591,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.3</version>
<version>3.12.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
@@ -86,7 +86,7 @@ public class Config extends YMLFile {
@ConfigDataString(path = "DiscordSRV.TopVoter.AllTime.RankDisplay")
@Getter
private String discordSRVTopVoterAllTimeRankDisplay = "%rank%: &6%player% - %votes% Votes";
private String discordSRVTopVoterAllTimeRankDisplay = "%rank%: %player% - %votes% Votes";
@ConfigDataBoolean(path = "DiscordSRV.TopVoter.Monthly.Enabled")
@Getter
@@ -102,7 +102,7 @@ public class Config extends YMLFile {
@ConfigDataString(path = "DiscordSRV.TopVoter.Monthly.RankDisplay")
@Getter
private String discordSRVTopVoterMonthlyRankDisplay = "%rank%: &6%player% - %votes% Votes";
private String discordSRVTopVoterMonthlyRankDisplay = "%rank%: %player% - %votes% Votes";
@ConfigDataBoolean(path = "DiscordSRV.TopVoter.Weekly.Enabled")
@Getter
@@ -118,7 +118,7 @@ public class Config extends YMLFile {
@ConfigDataString(path = "DiscordSRV.TopVoter.Weekly.RankDisplay")
@Getter
private String discordSRVTopVoterWeeklyRankDisplay = "%rank%: &6%player% - %votes% Votes";
private String discordSRVTopVoterWeeklyRankDisplay = "%rank%: %player% - %votes% Votes";
@ConfigDataBoolean(path = "DiscordSRV.TopVoter.Daily.Enabled")
@Getter
@@ -134,7 +134,7 @@ public class Config extends YMLFile {
@ConfigDataString(path = "DiscordSRV.TopVoter.Daily.RankDisplay")
@Getter
private String discordSRVTopVoterDailyRankDisplay = "%rank%: &6%player% - %votes% Votes";
private String discordSRVTopVoterDailyRankDisplay = "%rank%: %player% - %votes% Votes";
public boolean isDiscordSRVTopVoterEnabled(TopVoter topVoter) {
switch (topVoter) {
@@ -867,3 +867,4 @@ public class Config extends YMLFile {
}
}
@@ -11,6 +11,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import com.bencodez.advancedcore.api.bedrock.BedrockNameResolver;
import com.bencodez.advancedcore.api.misc.PlayerManager;
import com.bencodez.simpleapi.array.ArrayUtils;
import com.bencodez.votingplugin.VotingPluginMain;
@@ -51,34 +52,34 @@ public class PlayerVoteListener implements Listener {
String playerName = event.getPlayer();
plugin.debug("Processing PlayerVoteEvent: " + playerName + "/" + event.getServiceSite());
boolean notValid = false;
if (!PlayerManager.getInstance().isValidUser(playerName, plugin.getConfigFile().isAllowUnJoinedCheckServer())) {
if (!playerName.startsWith(plugin.getOptions().getBedrockPlayerPrefix())
&& !plugin.getOptions().getBedrockPlayerPrefix().isEmpty()) {
if (!event.isBungee() && PlayerManager.getInstance().isValidUser(
plugin.getOptions().getBedrockPlayerPrefix() + playerName,
plugin.getConfigFile().isAllowUnJoinedCheckServer())) {
plugin.debug("Detected a bedrock user without bedrock prefix, adjusting");
playerName = plugin.getOptions().getBedrockPlayerPrefix() + playerName;
} else {
notValid = true;
}
} else {
notValid = true;
}
}
// check for name casing
final String properName = plugin.getUserManager().getProperName(playerName);
if (notValid) {
// Resolve Bedrock/Java + add prefix only when appropriate
BedrockNameResolver.Result rn = plugin.getBedrockHandle().resolve(properName);
String creditedName = rn.finalName;
plugin.debug("Vote name resolved: " + properName + " -> " + creditedName + " (" + rn.rationale + ")");
// Single validity check on the final name
boolean allowUnJoinedCheckServer = plugin.getConfigFile().isAllowUnJoinedCheckServer();
boolean isValid = PlayerManager.getInstance().isValidUser(creditedName, allowUnJoinedCheckServer);
// Handle not-valid the same way you did before
if (!isValid) {
if (!plugin.getConfigFile().isAllowUnjoined()) {
plugin.getLogger().warning("Player " + playerName
+ " has not joined before, disregarding vote, set AllowUnjoined to true to prevent this");
plugin.getLogger().warning("Player " + creditedName + " has not joined before, disregarding vote. "
+ "Set AllowUnjoined to true to accept.");
if (event.isBungee() && plugin.getBungeeSettings().isRemoveInvalidUsers()) {
plugin.getVotingPluginUserManager().getVotingPluginUser(playerName).remove();
plugin.getVotingPluginUserManager().getVotingPluginUser(creditedName).remove();
}
return;
}
}
// If we get here, use the resolved/possibly-prefixed name going forward
playerName = creditedName;
if (playerName.isEmpty()) {
plugin.getLogger().warning("Empty player name from vote, ignoring");
return;
@@ -128,14 +129,6 @@ public class PlayerVoteListener implements Listener {
user = plugin.getVotingPluginUserManager().getVotingPluginUser(p);
} else {
// attempt to get exact name from cache matching casing
if (!plugin.getOptions().isOnlineMode()) {
for (Entry<String, String> entry : plugin.getUuidNameCache().entrySet()) {
if (entry.getValue().equalsIgnoreCase(playerName)) {
playerName = entry.getValue();
break;
}
}
}
user = plugin.getVotingPluginUserManager().getVotingPluginUser(playerName);
}
}
@@ -109,6 +109,11 @@ public class VoteSite {
if (plugin.getConfigFile().isFormatAlternateBroadcastEnabled()) {
return;
}
if (isHidden()) {
plugin.debug("Not broadcasting for " + user.getPlayerName() + ", site is hidden");
return;
}
if (!user.isVanished()) {
String playerName = user.getPlayerName();
if (plugin.getConfigFile().getVotingBroadcastBlacklist().contains(playerName)) {
@@ -1018,7 +1018,7 @@ public class PlaceHolders {
@Override
public String placeholderRequest(String identifier) {
LocalDateTime now = plugin.getTimeChecker().getTime();
LocalDateTime now = plugin.getTimeChecker().getTime().plusDays(plugin.getOptions().getTimeWeekOffSet());
LocalDateTime newWeek = plugin.getTimeChecker().getTime().withHour(0).withMinute(0);
TemporalField woy = WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear();
@@ -59,6 +59,9 @@ public abstract class ProxyMysqlUserTable {
if (config.getTablePrefix() != null) {
name = config.getTablePrefix() + tableName;
}
if (config.getPoolName().isEmpty()) {
config.setPoolName("VotingPlugin" + "-" + tableName);
}
mysql = new com.bencodez.simpleapi.sql.mysql.MySQL(config.getMaxThreads()) {
@Override
@@ -19,12 +19,9 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
@@ -53,6 +50,9 @@ import com.bencodez.simpleapi.sql.data.DataValue;
import com.bencodez.simpleapi.sql.data.DataValueBoolean;
import com.bencodez.simpleapi.sql.data.DataValueInt;
import com.bencodez.simpleapi.sql.data.DataValueString;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
import com.bencodez.votingplugin.proxy.cache.IVoteCache;
import com.bencodez.votingplugin.proxy.cache.VoteCacheHandler;
import com.bencodez.votingplugin.proxy.multiproxy.MultiProxyHandler;
import com.bencodez.votingplugin.proxy.multiproxy.MultiProxyMethod;
import com.bencodez.votingplugin.proxy.multiproxy.MultiProxyServerSocketConfiguration;
@@ -79,14 +79,6 @@ public abstract class VotingPluginProxy {
private EncryptionHandler encryptionHandler;
@Getter
@Setter
private ConcurrentHashMap<String, ArrayList<OfflineBungeeVote>> cachedOnlineVotes = new ConcurrentHashMap<>();
@Getter
@Setter
private ConcurrentHashMap<String, ArrayList<OfflineBungeeVote>> cachedVotes = new ConcurrentHashMap<>();
private HashMap<String, ClientHandler> clientHandles;
private SocketHandler socketHandler;
@@ -118,9 +110,6 @@ public abstract class VotingPluginProxy {
@Setter
private BungeeMethod method;
@Getter
private Queue<VoteTimeQueue> timeChangeQueue = new ConcurrentLinkedQueue<>();
@Getter
private MqttHandler mqttHandler;
@@ -130,36 +119,14 @@ public abstract class VotingPluginProxy {
@Getter
private ProxyMessenger proxyMysqlMessenger;
public void resetMilestoneCountInVotes() {
// Iterate through cached online votes
for (Map.Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedOnlineVotes.entrySet()) {
ArrayList<OfflineBungeeVote> votes = entry.getValue();
for (OfflineBungeeVote vote : votes) {
String updatedText = updateMilestoneCount(vote.getText());
vote.setText(updatedText);
}
}
// Iterate through cached votes
for (Map.Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedVotes.entrySet()) {
ArrayList<OfflineBungeeVote> votes = entry.getValue();
for (OfflineBungeeVote vote : votes) {
String updatedText = updateMilestoneCount(vote.getText());
vote.setText(updatedText);
}
}
}
private String updateMilestoneCount(String text) {
BungeeMessageData data = new BungeeMessageData(text);
data.setMilestoneCount(0); // Reset milestone count to 0
return data.toString();
}
@Getter
private VoteCacheHandler voteCacheHandler;
public VotingPluginProxy() {
enabled = true;
bungeeTimeChecker = new BungeeTimeChecker(getConfig().getTimeZone(), getConfig().getTimeHourOffSet()) {
bungeeTimeChecker = new BungeeTimeChecker(getConfig().getTimeZone(), getConfig().getTimeHourOffSet(),
getConfig().getTimeWeekOffSet()) {
@Override
public void debug(String text) {
@@ -230,11 +197,11 @@ public abstract class VotingPluginProxy {
public void timeChanged(TimeType type, boolean fake, boolean pre, boolean post) {
if (type.equals(TimeType.MONTH) && getConfig().getResetMilestonesMonthly()) {
debug("Resetting milestones for month change");
resetMilestoneCountInVotes();
getVoteCacheHandler().resetMilestoneCountInVotes();
}
if (getConfig().getVoteCacheTime() > 0) {
checkVoteCacheTime();
getVoteCacheHandler().checkVoteCacheTime(getConfig().getVoteCacheTime());
}
if (!getConfig().getGlobalDataEnabled()) {
warn("Global data not enabled, ignoring time change event");
@@ -303,9 +270,9 @@ public abstract class VotingPluginProxy {
int delay = 1;
if (isServerValid(server)) {
if (isSomeoneOnlineServer(server)) {
if (cachedVotes.containsKey(server) && !getConfig().getBlockedServers().contains(server)) {
ArrayList<OfflineBungeeVote> c = cachedVotes.get(server);
ArrayList<OfflineBungeeVote> newSet = new ArrayList<>();
if (getVoteCacheHandler().hasVotes(server) && !getConfig().getBlockedServers().contains(server)) {
ArrayList<OfflineBungeeVote> c = getVoteCacheHandler().getVotes(server);
ArrayList<OfflineBungeeVote> removed = new ArrayList<>();
if (!c.isEmpty()) {
int num = 1;
int numberOfVotes = c.size();
@@ -329,24 +296,31 @@ public abstract class VotingPluginProxy {
"" + num, "" + numberOfVotes);
delay++;
num++;
removed.add(cache);
} else {
debug("Not sending vote because user isn't on server " + server + ": "
+ cache.toString());
newSet.add(cache);
}
}
cachedVotes.put(server, newSet);
getVoteCacheHandler().removeServerVotes(server, removed);
} else {
debug("No cached votes for server: " + server);
}
} else {
debug("No cached votes for server: " + server);
}
}
} else {
debug("Server not valid: " + server);
}
}
public synchronized void checkOnlineVotes(String player, String uuid, String server) {
int delay = 1;
if (isPlayerOnline(player) && cachedOnlineVotes.containsKey(uuid)) {
ArrayList<OfflineBungeeVote> c = cachedOnlineVotes.get(uuid);
if (isPlayerOnline(player) && getVoteCacheHandler().hasOnlineVotes(uuid)) {
ArrayList<OfflineBungeeVote> c = getVoteCacheHandler().getOnlineVotes(uuid);
if (!c.isEmpty()) {
if (server == null) {
server = getCurrentPlayerServer(player);
@@ -363,7 +337,7 @@ public abstract class VotingPluginProxy {
delay++;
num++;
}
cachedOnlineVotes.put(uuid, new ArrayList<>());
getVoteCacheHandler().removeOnlineVotes(uuid);
if (getConfig().getMultiProxySupport() && getConfig().getMultiProxyOneGlobalReward()) {
multiProxyHandler.sendMultiProxyServerMessage("ClearVote", player, uuid);
}
@@ -372,29 +346,6 @@ public abstract class VotingPluginProxy {
}
}
public void checkVoteCacheTime() {
long cTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
for (Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedOnlineVotes.entrySet()) {
ArrayList<OfflineBungeeVote> votes = entry.getValue();
for (int i = votes.size() - 1; i >= 0; i--) {
if (votes.get(i).getTime() + (getConfig().getVoteCacheTime() * 24 * 60 * 60 * 1000) < cTime) {
debug("Removing vote from cache: " + votes.get(i).toString());
votes.remove(i);
}
}
}
for (Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedVotes.entrySet()) {
ArrayList<OfflineBungeeVote> votes = entry.getValue();
for (int i = votes.size() - 1; i >= 0; i--) {
if (votes.get(i).getTime() + (getConfig().getVoteCacheTime() * 24 * 60 * 60 * 1000) < cTime) {
debug("Removing vote from cache: " + votes.get(i).toString());
votes.remove(i);
}
}
}
}
public void checkVoteParty() {
if (getConfig().getVotePartyEnabled()) {
if (votePartyVotes >= currentVotePartyVotesRequired) {
@@ -523,12 +474,44 @@ public abstract class VotingPluginProxy {
public abstract boolean isVoteCacheIgnoreTime();
public void load() {
public abstract MysqlConfig getVoteCacheMySQLConfig();
public void load(IVoteCache jsonStorage) {
uuidPlayerNameCache = getProxyMySQL().getRowsUUIDNameQuery();
bungeeTimeChecker.setTimeChangeFailSafeBypass(getConfig().getTimeChangeFailSafeBypass());
bungeeTimeChecker.loadTimer();
voteCacheHandler = new VoteCacheHandler(getVoteCacheMySQLConfig(), getConfig().getVoteCacheUseMySQL(),
getConfig().getVoteCacheUseMainMySQL(), getProxyMySQL().getMysql(), getConfig().getDebug(),
jsonStorage) {
@Override
public void logInfo1(String msg) {
logInfo(msg);
}
@Override
public void logSevere1(String msg) {
logSevere(msg);
}
@Override
public void debug1(Exception e) {
if (getConfig().getDebug()) {
e.printStackTrace();
}
}
@Override
public void debug1(String msg) {
if (getConfig().getDebug()) {
debug(msg);
}
}
};
voteCacheHandler.load();
method = BungeeMethod.getByName(getConfig().getBungeeMethod());
if (getMethod() == null) {
method = BungeeMethod.PLUGINMESSAGING;
@@ -748,7 +731,7 @@ public abstract class VotingPluginProxy {
@Override
public void clearVote(String string) {
cachedOnlineVotes.remove(string);
getVoteCacheHandler().removeOnlineVotes(string);
}
@Override
@@ -897,6 +880,7 @@ public abstract class VotingPluginProxy {
public abstract void logSevere(String message);
public void onDisable() {
getVoteCacheHandler().saveVoteCache();
if (getProxyMysqlMessenger() != null) {
getProxyMysqlMessenger().shutdown();
@@ -982,8 +966,8 @@ public abstract class VotingPluginProxy {
}
public void processQueue() {
while (getTimeChangeQueue().size() > 0) {
VoteTimeQueue vote = getTimeChangeQueue().remove();
while (getVoteCacheHandler().getTimeChangeQueue().size() > 0) {
VoteTimeQueue vote = getVoteCacheHandler().getTimeChangeQueue().remove();
vote(vote.getName(), vote.getService(), true, false, vote.getTime(), null, null);
}
}
@@ -1149,8 +1133,8 @@ public abstract class VotingPluginProxy {
long mostRecentTime = 0;
// Check cached online votes
if (cachedOnlineVotes.containsKey(uuid)) {
ArrayList<OfflineBungeeVote> onlineVotes = cachedOnlineVotes.get(uuid);
if (getVoteCacheHandler().hasOnlineVotes(uuid)) {
ArrayList<OfflineBungeeVote> onlineVotes = getVoteCacheHandler().getOnlineVotes(uuid);
for (OfflineBungeeVote vote : onlineVotes) {
if (vote.getService().equalsIgnoreCase(service)) {
mostRecentTime = Math.max(mostRecentTime, vote.getTime());
@@ -1159,8 +1143,8 @@ public abstract class VotingPluginProxy {
}
// Check cached votes
for (ArrayList<OfflineBungeeVote> votes : cachedVotes.values()) {
for (OfflineBungeeVote vote : votes) {
for (String server : getAllAvailableServers()) {
for (OfflineBungeeVote vote : getVoteCacheHandler().getVotes(server)) {
if (vote.getUuid().equals(uuid) && vote.getService().equalsIgnoreCase(service)) {
mostRecentTime = Math.max(mostRecentTime, vote.getTime());
}
@@ -1249,7 +1233,7 @@ public abstract class VotingPluginProxy {
if (getGlobalDataHandler().isTimeChangedHappened()) {
getGlobalDataHandler().checkForFinishedTimeChanges();
if (timeQueue && getGlobalDataHandler().isTimeChangedHappened()) {
timeChangeQueue.add(new VoteTimeQueue(player, service,
getVoteCacheHandler().getTimeChangeQueue().add(new VoteTimeQueue(player, service,
LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()));
log("Cachcing vote from " + player + "/" + service
+ " because time change is happening right now");
@@ -1390,13 +1374,9 @@ public abstract class VotingPluginProxy {
globalMessageProxyHandler.sendMessage(s, 1, "VoteBroadcast", uuid, player, service);
}
if ((!isSomeoneOnlineServer(s) && method.requiresPlayerOnline()) || forceCache) {
// cache
if (!cachedVotes.containsKey(s)) {
cachedVotes.put(s, new ArrayList<>());
}
ArrayList<OfflineBungeeVote> list = cachedVotes.get(s);
list.add(new OfflineBungeeVote(player, uuid, service, time, realVote, text.toString()));
cachedVotes.put(s, list);
getVoteCacheHandler().addServerVote(s,
new OfflineBungeeVote(player, uuid, service, time, realVote, text.toString()));
debug("Caching vote for " + player + " on " + service + " for " + s);
@@ -1419,15 +1399,8 @@ public abstract class VotingPluginProxy {
multiProxyHandler.sendMultiProxyServerMessage("ClearVote", player, uuid);
}
} else {
if (!cachedOnlineVotes.containsKey(uuid)) {
cachedOnlineVotes.put(uuid, new ArrayList<>());
}
ArrayList<OfflineBungeeVote> list = cachedOnlineVotes.get(uuid);
if (list == null) {
list = new ArrayList<>();
}
list.add(new OfflineBungeeVote(player, uuid, service, time, realVote, text.toString()));
cachedOnlineVotes.put(uuid, list);
getVoteCacheHandler().addOnlineVote(uuid,
new OfflineBungeeVote(player, uuid, service, time, realVote, text.toString()));
debug("Caching online vote for " + player + " on " + service);
}
int delay = 2;
@@ -27,6 +27,10 @@ public interface VotingPluginProxyConfig {
public boolean getPluginMessageEncryption();
public boolean getDebug();
public boolean getVoteCacheUseMySQL();
public boolean getVoteCacheUseMainMySQL();
public String getMqttClientID();
@@ -103,6 +107,8 @@ public interface VotingPluginProxyConfig {
public boolean getTimeChangeFailSafeBypass();
public int getTimeHourOffSet();
public int getTimeWeekOffSet();
public String getTimeZone();
@@ -409,4 +409,19 @@ public class Config implements VotingPluginProxyConfig {
return getData().getBoolean("ResetMilestonesMonthly", false);
}
@Override
public boolean getVoteCacheUseMySQL() {
return getData().getBoolean("VoteCache.UseMySQL", false);
}
@Override
public boolean getVoteCacheUseMainMySQL() {
return getData().getBoolean("VoteCache.UseMainMySQL", true);
}
@Override
public int getTimeWeekOffSet() {
return getData().getInt("TimeWeekOffSet");
}
}
@@ -5,10 +5,12 @@ import java.util.Collection;
import com.bencodez.simpleapi.file.BungeeJsonFile;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
import com.bencodez.votingplugin.proxy.cache.DataNode;
import com.bencodez.votingplugin.proxy.cache.GsonDataNode;
import com.bencodez.votingplugin.proxy.cache.IVoteCache;
import com.bencodez.votingplugin.timequeue.VoteTimeQueue;
import com.google.gson.JsonElement;
public class VoteCache extends BungeeJsonFile {
public class VoteCache extends BungeeJsonFile implements IVoteCache {
private VotingPluginBungee bungee;
public VoteCache(VotingPluginBungee bungee) {
@@ -63,8 +65,8 @@ public class VoteCache extends BungeeJsonFile {
return getKeys("OnlineCache." + name);
}
public JsonElement getOnlineVotes(String name, String num) {
return getNode("OnlineCache." + name + "." + num);
public GsonDataNode getOnlineVotes(String name, String num) {
return new GsonDataNode(getNode("OnlineCache." + name + "." + num));
}
public Collection<String> getPlayers() {
@@ -79,16 +81,16 @@ public class VoteCache extends BungeeJsonFile {
return getKeys("VoteCache." + server);
}
public JsonElement getServerVotes(String server, String num) {
return getNode("VoteCache." + server + "." + num);
public GsonDataNode getServerVotes(String server, String num) {
return new GsonDataNode(getNode("VoteCache." + server + "." + num));
}
public Collection<String> getTimedVoteCache() {
return getKeys("TimedVoteCache");
}
public JsonElement getTimedVoteCache(String key) {
return getNode("TimedVoteCache." + key);
public DataNode getTimedVoteCache(String key) {
return new GsonDataNode(getNode("TimedVoteCache." + key));
}
public int getVotePartyCache(String server) {
@@ -124,4 +126,98 @@ public class VoteCache extends BungeeJsonFile {
public void reload() {
super.reload();
}
@Override
public void removeOnlineVotes(String player) {
setString("OnlineCache." + player, null);
}
@Override
public void removeServerVotes(String server) {
setString("VoteCache." + server, null);
}
@Override
public void removeServerVote(String server, String uuid) {
Collection<String> votes = getServerVotes(server);
if (votes == null) {
return;
}
// search for vote with uuid and remove it
for (String num : votes) {
GsonDataNode node = getServerVotes(server, num);
if (node == null) {
continue;
}
DataNode uuidNode = node.get("UUID");
if (uuidNode == null) {
continue;
}
String nodeUuid = uuidNode.asString();
if (nodeUuid != null && nodeUuid.equals(uuid)) {
setString("VoteCache." + server + "." + num, null);
}
}
}
@Override
public void removeVote(String server, OfflineBungeeVote vote) {
Collection<String> votes = getServerVotes(server);
if (votes == null) {
return;
}
for (String num : votes) {
GsonDataNode node = getServerVotes(server, num);
if (node == null) {
continue;
}
DataNode uuidNode = node.get("UUID");
DataNode serviceNode = node.get("Service");
DataNode timeNode = node.get("Time");
if (uuidNode == null || serviceNode == null || timeNode == null) {
continue;
}
String nodeUuid = uuidNode.asString();
String nodeService = serviceNode.asString();
Long nodeTime = timeNode.asLong();
if (nodeUuid != null && nodeService != null && nodeTime != null && nodeUuid.equals(vote.getUuid())
&& nodeService.equals(vote.getService()) && nodeTime.longValue() == vote.getTime()) {
setString("VoteCache." + server + "." + num, null);
}
}
}
@Override
public void removeOnlineVote(OfflineBungeeVote vote) {
Collection<String> players = getPlayers();
if (players == null) {
return;
}
for (String player : players) {
Collection<String> onlineVotes = getOnlineVotes(player);
if (onlineVotes == null) {
continue;
}
for (String num : onlineVotes) {
GsonDataNode node = getOnlineVotes(player, num);
if (node == null) {
continue;
}
DataNode uuidNode = node.get("UUID");
DataNode serviceNode = node.get("Service");
DataNode timeNode = node.get("Time");
if (uuidNode == null || serviceNode == null || timeNode == null) {
continue;
}
String nodeUuid = uuidNode.asString();
String nodeService = serviceNode.asString();
Long nodeTime = timeNode.asLong();
if (nodeUuid != null && nodeService != null && nodeTime != null && nodeUuid.equals(vote.getUuid())
&& nodeService.equals(vote.getService()) && nodeTime.longValue() == vote.getTime()) {
setString("OnlineCache." + player + "." + num, null);
}
}
}
}
}
@@ -25,17 +25,13 @@ import com.bencodez.advancedcore.api.time.TimeType;
import com.bencodez.advancedcore.bungeeapi.globaldata.GlobalDataHandlerProxy;
import com.bencodez.advancedcore.bungeeapi.globaldata.GlobalMySQL;
import com.bencodez.simpleapi.sql.DataType;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfigBungee;
import com.bencodez.votingplugin.proxy.BungeeMethod;
import com.bencodez.votingplugin.proxy.BungeeVersion;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
import com.bencodez.votingplugin.proxy.ProxyMysqlUserTable;
import com.bencodez.votingplugin.proxy.VotingPluginProxy;
import com.bencodez.votingplugin.proxy.VotingPluginProxyConfig;
import com.bencodez.votingplugin.timequeue.VoteTimeQueue;
import com.bencodez.votingplugin.topvoter.TopVoter;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.Getter;
import net.md_5.bungee.api.ChatColor;
@@ -220,6 +216,12 @@ public class VotingPluginBungee extends Plugin implements Listener {
if (type.equals(TimeType.MONTH)) {
getVotingPluginProxy().getProxyMySQL().copyColumnData(TopVoter.Monthly.getColumnName(),
"LastMonthTotal");
if (getVotingPluginProxy().getConfig().getResetMilestonesMonthly()) {
getVotingPluginProxy().getProxyMySQL().wipeColumnData("MilestoneCount",
DataType.INTEGER);
getVotingPluginProxy().getProxyMySQL().wipeColumnData("GottenMileStones",
DataType.STRING);
}
}
getVotingPluginProxy().getProxyMySQL().wipeColumnData(TopVoter.of(type).getColumnName(),
DataType.INTEGER);
@@ -285,6 +287,12 @@ public class VotingPluginBungee extends Plugin implements Listener {
if (type.equals(TimeType.MONTH)) {
getVotingPluginProxy().getProxyMySQL()
.copyColumnData(TopVoter.Monthly.getColumnName(), "LastMonthTotal");
if (getVotingPluginProxy().getConfig().getResetMilestonesMonthly()) {
getVotingPluginProxy().getProxyMySQL().wipeColumnData("MilestoneCount",
DataType.INTEGER);
getVotingPluginProxy().getProxyMySQL().wipeColumnData("GottenMileStones",
DataType.STRING);
}
}
getVotingPluginProxy().getProxyMySQL().wipeColumnData(TopVoter.of(type).getColumnName(),
DataType.INTEGER);
@@ -377,36 +385,6 @@ public class VotingPluginBungee extends Plugin implements Listener {
@Override
public void onDisable() {
if (getVotingPluginProxy().getMethod().equals(BungeeMethod.PLUGINMESSAGING)) {
getLogger().info("VotingPlugin saving vote cache: " + getVotingPluginProxy().getCachedVotes().size() + "/"
+ getVotingPluginProxy().getCachedOnlineVotes().size());
for (Entry<String, ArrayList<OfflineBungeeVote>> entry : getVotingPluginProxy().getCachedVotes()
.entrySet()) {
String server = entry.getKey();
int num = 0;
for (OfflineBungeeVote voteData : entry.getValue()) {
voteCacheFile.addVote(server, num, voteData);
num++;
}
}
for (Entry<String, ArrayList<OfflineBungeeVote>> entry : getVotingPluginProxy().getCachedOnlineVotes()
.entrySet()) {
String name = entry.getKey();
int num = 0;
for (OfflineBungeeVote voteData : entry.getValue()) {
voteCacheFile.addVoteOnline(name, num, voteData);
num++;
}
}
}
if (!getVotingPluginProxy().getTimeChangeQueue().isEmpty()) {
int num = 0;
for (VoteTimeQueue vote : getVotingPluginProxy().getTimeChangeQueue()) {
voteCacheFile.addTimedVote(num, vote);
num++;
}
}
getVotingPluginProxy().onDisable();
voteCacheFile.save();
@@ -651,6 +629,11 @@ public class VotingPluginBungee extends Plugin implements Listener {
return timer;
}
@Override
public MysqlConfig getVoteCacheMySQLConfig() {
return new MysqlConfigBungee(config.getData().getSection("VoteCache"));
}
};
try {
Class.forName("com.vexsoftware.votifier.bungee.events.VotifierEvent");
@@ -689,94 +672,20 @@ public class VotingPluginBungee extends Plugin implements Listener {
nonVotedPlayersCache = new NonVotedPlayersCache(this);
// nonVotedPlayersCache.load();
getVotingPluginProxy().load();
try {
for (String key : voteCacheFile.getTimedVoteCache()) {
JsonElement dataElement = voteCacheFile.getTimedVoteCache(key);
if (dataElement != null && dataElement.isJsonObject()) {
JsonObject data = dataElement.getAsJsonObject();
String name = data.has("Name") ? data.get("Name").getAsString() : "";
String service = data.has("Service") ? data.get("Service").getAsString() : "";
long time = data.has("Time") ? data.get("Time").getAsLong() : 0L;
getVotingPluginProxy().getTimeChangeQueue().add(new VoteTimeQueue(name, service, time));
}
}
getVotingPluginProxy().processQueue();
} catch (Exception e) {
e.printStackTrace();
}
try {
for (String server : voteCacheFile.getServers()) {
ArrayList<OfflineBungeeVote> votes = new ArrayList<>();
for (String num : voteCacheFile.getServerVotes(server)) {
JsonElement dataElement = voteCacheFile.getServerVotes(server, num);
if (dataElement != null && dataElement.isJsonObject()) {
JsonObject data = dataElement.getAsJsonObject();
String name = data.has("Name") ? data.get("Name").getAsString() : "";
String uuid = data.has("UUID") ? data.get("UUID").getAsString() : "";
String service = data.has("Service") ? data.get("Service").getAsString() : "";
long time = data.has("Time") ? data.get("Time").getAsLong() : 0L;
boolean real = data.has("Real") && data.get("Real").getAsBoolean();
String text = data.has("Text") ? data.get("Text").getAsString() : "";
votes.add(new OfflineBungeeVote(name, uuid, service, time, real, text));
}
}
getVotingPluginProxy().getCachedVotes().put(server, votes);
}
} catch (Exception e) {
e.printStackTrace();
}
try {
for (String player : voteCacheFile.getPlayers()) {
ArrayList<OfflineBungeeVote> votes = new ArrayList<>();
for (String num : voteCacheFile.getOnlineVotes(player)) {
JsonElement dataElement = voteCacheFile.getOnlineVotes(player, num);
if (dataElement != null && dataElement.isJsonObject()) {
JsonObject data = dataElement.getAsJsonObject();
String name = data.has("Name") ? data.get("Name").getAsString() : "";
String uuid = data.has("UUID") ? data.get("UUID").getAsString() : "";
String service = data.has("Service") ? data.get("Service").getAsString() : "";
long time = data.has("Time") ? data.get("Time").getAsLong() : 0L;
boolean real = data.has("Real") && data.get("Real").getAsBoolean();
String text = data.has("Text") ? data.get("Text").getAsString() : "";
votes.add(new OfflineBungeeVote(name, uuid, service, time, real, text));
}
}
getLogger().info("VoteCache: " + player);
getVotingPluginProxy().getCachedOnlineVotes().put(player, votes);
}
} catch (Exception e) {
e.printStackTrace();
}
voteCacheFile.clearData();
getVotingPluginProxy().load(voteCacheFile);
getProxy().getScheduler().schedule(this, new Runnable() {
@Override
public void run() {
for (String server : getVotingPluginProxy().getCachedVotes().keySet()) {
for (String server : getVotingPluginProxy().getVoteCacheHandler().getCachedVotesServers()) {
getVotingPluginProxy().checkCachedVotes(server);
}
for (String player : getVotingPluginProxy().getCachedOnlineVotes().keySet()) {
ProxiedPlayer p = getProxy().getPlayer(UUID.fromString(player));
if (p != null) {
getVotingPluginProxy().checkOnlineVotes(p.getName(), player, null);
}
for (ProxiedPlayer player : getProxy().getPlayers()) {
getVotingPluginProxy().checkOnlineVotes(player.getName(), player.getUniqueId().toString(),
null);
}
}
}, 120l, 60l, TimeUnit.SECONDS);
@@ -0,0 +1,92 @@
package com.bencodez.votingplugin.proxy.cache;
import ninja.leaping.configurate.ConfigurationNode;
public class ConfigDataNode implements DataNode {
private final ConfigurationNode node;
public ConfigDataNode(ConfigurationNode node) {
this.node = node;
}
@SuppressWarnings("deprecation")
@Override
public boolean isObject() {
return node.hasMapChildren();
}
@SuppressWarnings("deprecation")
@Override
public boolean isArray() {
return node.hasListChildren();
}
@Override
public boolean isPrimitive() {
return !isObject() && !isArray();
}
@Override
public DataNode get(String key) {
return new ConfigDataNode(node.getNode((Object) key));
}
@Override
public long asLong() {
return node.getLong();
}
@Override
public boolean has(String key) {
ConfigurationNode child = node.getNode((Object) key);
return child != null && child.getValue() != null;
}
@Override
public boolean has(int index) {
ConfigurationNode child = node.getNode(index);
return child != null && child.getValue() != null;
}
@Override
public DataNode get(int index) {
return new ConfigDataNode(node.getNode(index));
}
@Override
public void set(String key, Object value) {
node.getNode((Object) key).setValue(value);
}
@Override
public void set(int index, Object value) {
node.getNode(index).setValue(value);
}
@Override
public Object asPrimitive() {
return node.getValue();
}
@Override
public String asString() {
return node.getString();
}
@Override
public int asInt() {
return node.getInt();
}
@Override
public boolean asBoolean() {
return node.getBoolean();
}
@Override
public Object toInternal() {
return node;
}
}
@@ -0,0 +1,32 @@
package com.bencodez.votingplugin.proxy.cache;
public interface DataNode {
boolean isObject();
boolean isArray();
boolean isPrimitive();
DataNode get(String key);
DataNode get(int index);
void set(String key, Object value);
void set(int index, Object value);
Object asPrimitive();
String asString();
long asLong();
boolean has(String key);
boolean has(int index);
int asInt();
boolean asBoolean();
Object toInternal(); // Returns either JsonElement or ConfigurationNode
}
@@ -0,0 +1,118 @@
package com.bencodez.votingplugin.proxy.cache;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
public class GsonDataNode implements DataNode {
private final JsonElement element;
public GsonDataNode(JsonElement element) {
this.element = element;
}
@Override
public boolean isObject() {
return element.isJsonObject();
}
@Override
public boolean isArray() {
return element.isJsonArray();
}
@Override
public boolean isPrimitive() {
return element.isJsonPrimitive();
}
@Override
public DataNode get(String key) {
if (!isObject())
return null;
return new GsonDataNode(element.getAsJsonObject().get(key));
}
@Override
public DataNode get(int index) {
if (!isArray())
return null;
return new GsonDataNode(element.getAsJsonArray().get(index));
}
@Override
public void set(String key, Object value) {
if (!isObject())
return;
element.getAsJsonObject().add(key, new Gson().toJsonTree(value));
}
@Override
public void set(int index, Object value) {
if (!isArray())
return;
element.getAsJsonArray().set(index, new Gson().toJsonTree(value));
}
@Override
public long asLong() {
if (element == null || !element.isJsonPrimitive())
return 0L;
JsonPrimitive prim = element.getAsJsonPrimitive();
if (prim.isNumber()) {
return prim.getAsLong();
}
// Try parsing from string if it’s not a number
try {
return Long.parseLong(prim.getAsString());
} catch (NumberFormatException e) {
return 0L;
}
}
@Override
public Object asPrimitive() {
if (!isPrimitive())
return null;
JsonPrimitive prim = element.getAsJsonPrimitive();
if (prim.isBoolean())
return prim.getAsBoolean();
if (prim.isNumber())
return prim.getAsNumber();
return prim.getAsString();
}
@Override
public boolean has(String key) {
return isObject() && element.getAsJsonObject().has(key);
}
@Override
public boolean has(int index) {
return isArray() && index >= 0 && index < element.getAsJsonArray().size();
}
@Override
public String asString() {
return element.getAsString();
}
@Override
public int asInt() {
return element.getAsInt();
}
@Override
public boolean asBoolean() {
return element.getAsBoolean();
}
@Override
public Object toInternal() {
return element;
}
}
@@ -0,0 +1,59 @@
package com.bencodez.votingplugin.proxy.cache;
import java.util.Collection;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
import com.bencodez.votingplugin.timequeue.VoteTimeQueue;
public interface IVoteCache {
void addTimedVote(int num, VoteTimeQueue voteTimedQueue);
void addVote(String server, int num, OfflineBungeeVote voteData);
void addVoteOnline(String player, int num, OfflineBungeeVote voteData);
void clearData();
Collection<String> getOnlineVotes(String name);
DataNode getOnlineVotes(String name, String num);
Collection<String> getPlayers();
Collection<String> getServers();
Collection<String> getServerVotes(String server);
DataNode getServerVotes(String server, String num);
Collection<String> getTimedVoteCache();
DataNode getTimedVoteCache(String key);
int getVotePartyCache(String server);
int getVotePartyCurrentVotes();
int getVotePartyInreaseVotesRequired();
void setVotePartyCache(String server, int amount);
void setVotePartyCurrentVotes(int amount);
void setVotePartyInreaseVotesRequired(int amount);
void save();
void reload();
void removeOnlineVotes(String player);
void removeServerVotes(String server);
void removeServerVote(String server, String uuid);
void removeVote(String server, OfflineBungeeVote vote);
void removeOnlineVote(OfflineBungeeVote vote);
}
@@ -0,0 +1,240 @@
package com.bencodez.votingplugin.proxy.cache;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
import com.bencodez.simpleapi.sql.mysql.queries.Query;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
public abstract class ProxyOnlineVoteCacheTable {
protected com.bencodez.simpleapi.sql.mysql.MySQL mysql;
private final String tableName;
public abstract void logSevere(String msg);
public abstract void logInfo(String msg);
public abstract void debug(Exception e);
public ProxyOnlineVoteCacheTable(com.bencodez.simpleapi.sql.mysql.MySQL existingMysql, String tablePrefix,
boolean debug) {
this.tableName = (tablePrefix != null ? tablePrefix : "") + "votingplugin_onlinevotecache";
this.mysql = existingMysql;
// Create table if not exists
String sql = "CREATE TABLE IF NOT EXISTS `" + tableName + "` (" + "id INT AUTO_INCREMENT PRIMARY KEY,"
+ "uuid VARCHAR(36)," + "playerName VARCHAR(100)," + "service VARCHAR(100)," + "`time` BIGINT,"
+ "realvote BOOLEAN," + "text LONGTEXT," + "INDEX idx_uuid (`uuid`)," + "INDEX idx_time (`time`)"
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
try {
new Query(mysql, sql).executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public ProxyOnlineVoteCacheTable(MysqlConfig config, boolean debug) {
String prefix = config.getTablePrefix() != null ? config.getTablePrefix() : "";
this.tableName = prefix + "votingplugin_onlinevotecache";
mysql = new com.bencodez.simpleapi.sql.mysql.MySQL(config.getMaxThreads()) {
@Override
public void debug(SQLException e) {
if (debug)
e.printStackTrace();
}
@Override
public void severe(String msg) {
logSevere(msg);
}
@Override
public void debug(String msg) {
if (debug)
logInfo("MYSQL DEBUG: " + msg);
}
};
if (!mysql.connect(config)) {
logSevere("Failed to connect to MySQL for online vote cache!");
}
try {
new Query(mysql, "USE `" + config.getDatabase() + "`;").executeUpdate();
} catch (SQLException e) {
logSevere("Failed to select database: " + config.getDatabase());
debug(e);
}
String sql = "CREATE TABLE IF NOT EXISTS `" + tableName + "` (" + "id INT AUTO_INCREMENT PRIMARY KEY,"
+ "uuid VARCHAR(36)," + "playerName VARCHAR(100)," + "service VARCHAR(100)," + "`time` BIGINT,"
+ "realvote BOOLEAN," + "text LONGTEXT," + "INDEX idx_uuid (`uuid`)," + "INDEX idx_time (`time`)"
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
try {
new Query(mysql, sql).executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getTableName() {
return tableName;
}
// --- INSERT ---
public void insertOnlineVote(String uuid, String playerName, String service, long time, boolean realvote,
String text) {
String sql = "INSERT INTO `" + tableName + "` (uuid, playerName, service, `time`, realvote, text) "
+ "VALUES (?, ?, ?, ?, ?, ?);";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, uuid);
ps.setString(2, playerName);
ps.setString(3, service);
ps.setLong(4, time);
ps.setBoolean(5, realvote);
ps.setString(6, text);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
// --- GET ---
public List<OnlineVoteRow> getAllVotes() {
String sql = "SELECT * FROM `" + tableName + "`;";
return selectVotes(sql, null);
}
public List<OnlineVoteRow> getVotesForUUID(String uuid) {
String sql = "SELECT * FROM `" + tableName + "` WHERE `uuid` = ?;";
return selectVotes(sql, new Object[] { uuid });
}
// --- DELETE ---
public void removeVoteById(int id) {
String sql = "DELETE FROM `" + tableName + "` WHERE id = ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, id);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void removeVotesForUUID(String uuid) {
String sql = "DELETE FROM `" + tableName + "` WHERE uuid = ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, uuid);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void clearTable() {
try {
new Query(mysql, "TRUNCATE TABLE `" + tableName + "`;").executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close() {
mysql.disconnect();
}
private List<OnlineVoteRow> selectVotes(String sql, Object[] params) {
List<OnlineVoteRow> list = new ArrayList<>();
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
OnlineVoteRow v = new OnlineVoteRow(rs.getInt("id"), rs.getString("uuid"),
rs.getString("playerName"), rs.getString("service"), rs.getLong("time"),
rs.getBoolean("realvote"), rs.getString("text"));
list.add(v);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
public void removeOnlineVote(OfflineBungeeVote vote) {
String sql = "DELETE FROM `" + tableName + "` WHERE `uuid` = ? AND `service` = ? AND `time` = ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, vote.getUuid());
ps.setString(2, vote.getService());
ps.setLong(3, vote.getTime());
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static class OnlineVoteRow {
private final int id;
private final String uuid;
private final String playerName;
private final String service;
private final long time;
private final boolean realvote;
private final String text;
public OnlineVoteRow(int id, String uuid, String playerName, String service, long time, boolean realvote,
String text) {
this.id = id;
this.uuid = uuid;
this.playerName = playerName;
this.service = service;
this.time = time;
this.realvote = realvote;
this.text = text;
}
public int getId() {
return id;
}
public String getUuid() {
return uuid;
}
public String getPlayerName() {
return playerName;
}
public String getService() {
return service;
}
public long getTime() {
return time;
}
public boolean isRealvote() {
return realvote;
}
public String getText() {
return text;
}
}
}
@@ -0,0 +1,200 @@
package com.bencodez.votingplugin.proxy.cache;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
import com.bencodez.simpleapi.sql.mysql.queries.Query;
public abstract class ProxyTimedVoteCacheTable {
protected com.bencodez.simpleapi.sql.mysql.MySQL mysql;
private final String tableName;
public abstract void logSevere(String msg);
public abstract void logInfo(String msg);
public abstract void debug(Exception e);
public ProxyTimedVoteCacheTable(com.bencodez.simpleapi.sql.mysql.MySQL existingMysql, String tablePrefix,
boolean debug) {
this.tableName = (tablePrefix != null ? tablePrefix : "") + "votingplugin_timedvotecache";
this.mysql = existingMysql;
// Create table if not exists
String sql = "CREATE TABLE IF NOT EXISTS `" + tableName + "` (" + "id INT AUTO_INCREMENT PRIMARY KEY,"
+ "playerName VARCHAR(100)," + "service VARCHAR(100)," + "time BIGINT," + "INDEX idx_time (time)"
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
try {
new Query(mysql, sql).executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public ProxyTimedVoteCacheTable(MysqlConfig config, boolean debug) {
String prefix = config.getTablePrefix() != null ? config.getTablePrefix() : "";
this.tableName = prefix + "votingplugin_timedvotecache";
mysql = new com.bencodez.simpleapi.sql.mysql.MySQL(config.getMaxThreads()) {
@Override
public void debug(SQLException e) {
if (debug)
e.printStackTrace();
}
@Override
public void severe(String msg) {
logSevere(msg);
}
@Override
public void debug(String msg) {
if (debug)
logInfo("MYSQL DEBUG: " + msg);
}
};
if (!mysql.connect(config)) {
logSevere("Failed to connect to MySQL for timed vote cache!");
}
try {
new Query(mysql, "USE `" + config.getDatabase() + "`;").executeUpdate();
} catch (SQLException e) {
logSevere("Failed to select database: " + config.getDatabase());
debug(e);
}
// Updated table creation without uuid, server, realVote
String sql = "CREATE TABLE IF NOT EXISTS `" + tableName + "` (" + "id INT AUTO_INCREMENT PRIMARY KEY,"
+ "playerName VARCHAR(100)," + "service VARCHAR(100)," + "time BIGINT," + "INDEX idx_time (time)"
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
try {
new Query(mysql, sql).executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getTableName() {
return tableName;
}
// --- INSERT ---
public void insertTimedVote(String playerName, String service, long time) {
String sql = "INSERT INTO `" + tableName + "` (playerName, service, time) VALUES (?, ?, ?);";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, playerName);
ps.setString(2, service);
ps.setLong(3, time);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
// --- GET ---
public List<TimedVoteRow> getAllVotes() {
String sql = "SELECT * FROM `" + tableName + "`;";
return selectVotes(sql, null);
}
public List<TimedVoteRow> getExpiredVotes(long now) {
String sql = "SELECT * FROM `" + tableName + "` WHERE time <= ?;";
return selectVotes(sql, new Object[] { now });
}
// --- DELETE ---
public void removeVoteById(int id) {
String sql = "DELETE FROM `" + tableName + "` WHERE id = ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, id);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void removeExpiredVotes(long now) {
String sql = "DELETE FROM `" + tableName + "` WHERE time <= ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, now);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void clearTable() {
try {
new Query(mysql, "TRUNCATE TABLE `" + tableName + "`;").executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close() {
mysql.disconnect();
}
private List<TimedVoteRow> selectVotes(String sql, Object[] params) {
List<TimedVoteRow> list = new ArrayList<>();
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
TimedVoteRow v = new TimedVoteRow(rs.getInt("id"), rs.getString("playerName"),
rs.getString("service"), rs.getLong("time"));
list.add(v);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
public static class TimedVoteRow {
private final int id;
private final String playerName;
private final String service;
private final long time;
public TimedVoteRow(int id, String playerName, String service, long time) {
this.id = id;
this.playerName = playerName;
this.service = service;
this.time = time;
}
public int getId() {
return id;
}
public String getPlayerName() {
return playerName;
}
public String getService() {
return service;
}
public long getTime() {
return time;
}
}
}
@@ -0,0 +1,303 @@
package com.bencodez.votingplugin.proxy.cache;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
import com.bencodez.simpleapi.sql.mysql.queries.Query;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
import lombok.Getter;
public abstract class ProxyVoteCacheTable {
@Getter
private com.bencodez.simpleapi.sql.mysql.MySQL mysql;
private final String tableName;
public abstract void logSevere(String msg);
public abstract void logInfo(String msg);
public abstract void debug(Exception e);
public ProxyVoteCacheTable(com.bencodez.simpleapi.sql.mysql.MySQL existingMysql, String tablePrefix,
boolean debug) {
this.tableName = (tablePrefix != null ? tablePrefix : "") + "votingplugin_votecache";
this.mysql = existingMysql;
// Create table if not exists
String sql = "CREATE TABLE IF NOT EXISTS `" + tableName + "` (" + "id INT AUTO_INCREMENT PRIMARY KEY,"
+ "uuid VARCHAR(36)," + "playerName VARCHAR(100)," + "service VARCHAR(100)," + "`time` BIGINT,"
+ "realVote TINYINT(1)," + "`text` TEXT," + "`server` VARCHAR(100)," + "INDEX idx_server (`server`),"
+ "INDEX idx_uuid (`uuid`)," + "INDEX idx_time (`time`)" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
try {
new Query(mysql, sql).executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public ProxyVoteCacheTable(MysqlConfig config, boolean debug) {
// Always use fixed table name + optional prefix to avoid collisions with other
// tables
String prefix = config.getTablePrefix() != null ? config.getTablePrefix() : "";
this.tableName = prefix + "votingplugin_votecache";
mysql = new com.bencodez.simpleapi.sql.mysql.MySQL(config.getMaxThreads()) {
@Override
public void debug(SQLException e) {
if (debug)
e.printStackTrace();
}
@Override
public void severe(String msg) {
logSevere(msg);
}
@Override
public void debug(String msg) {
if (debug)
logInfo("MYSQL DEBUG: " + msg);
}
};
if (!mysql.connect(config)) {
logSevere("Failed to connect to MySQL for vote cache!");
}
try {
new Query(mysql, "USE `" + config.getDatabase() + "`;").executeUpdate();
} catch (SQLException e) {
logSevere("Failed to select database: " + config.getDatabase());
debug(e);
}
String sql = "CREATE TABLE IF NOT EXISTS `" + tableName + "` (" + "id INT AUTO_INCREMENT PRIMARY KEY,"
+ "uuid VARCHAR(36)," + "playerName VARCHAR(100)," + "service VARCHAR(100)," + "`time` BIGINT," // epoch
// millis
+ "realVote TINYINT(1)," // boolean
+ "`text` TEXT," + "`server` VARCHAR(100)," + "INDEX idx_server (`server`),"
+ "INDEX idx_uuid (`uuid`)," + "INDEX idx_time (`time`)" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
try {
new Query(mysql, sql).executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getTableName() {
return tableName;
}
// --- INSERT ---
public void insertVote(String uuid, String playerName, String service, long time, boolean real, String text,
String server) {
String sql = "INSERT INTO `" + tableName + "` (uuid, playerName, service, `time`, realVote, `text`, `server`) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?);";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, uuid);
ps.setString(2, playerName);
ps.setString(3, service);
ps.setLong(4, time);
ps.setBoolean(5, real);
ps.setString(6, text);
ps.setString(7, server);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
// --- GET ---
public List<VoteRow> getAllVotes() {
String sql = "SELECT * FROM `" + tableName + "`;";
return selectVotes(sql, null);
}
public List<VoteRow> getVotesForServer(String server) {
String sql = "SELECT * FROM `" + tableName + "` WHERE `server` = ?;";
return selectVotes(sql, new Object[] { server });
}
public VoteRow getVoteById(int id) {
String sql = "SELECT * FROM `" + tableName + "` WHERE id = ?;";
List<VoteRow> list = selectVotes(sql, new Object[] { id });
return list.isEmpty() ? null : list.get(0);
}
public List<VoteRow> getVotesByUUID(String uuid) {
String sql = "SELECT * FROM `" + tableName + "` WHERE uuid = ?;";
return selectVotes(sql, new Object[] { uuid });
}
public List<VoteRow> getVotesByPlayerName(String playerName) {
String sql = "SELECT * FROM `" + tableName + "` WHERE playerName = ?;";
return selectVotes(sql, new Object[] { playerName });
}
public Set<String> getServers() {
Set<String> servers = new HashSet<String>();
String sql = "SELECT DISTINCT `server` FROM `" + tableName + "` WHERE `server` IS NOT NULL;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String s = rs.getString(1);
if (s != null)
servers.add(s);
}
} catch (SQLException e) {
e.printStackTrace();
}
return servers;
}
// --- DELETE ---
public void removeVoteById(int id) {
String sql = "DELETE FROM `" + tableName + "` WHERE id = ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, id);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void removeVotesByServerAndUUID(String server, String uuid) {
String sql = "DELETE FROM `" + tableName + "` WHERE `server` = ? AND `uuid` = ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, server);
ps.setString(2, uuid);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void removeVote(OfflineBungeeVote vote, String server) {
String sql = "DELETE FROM `" + tableName
+ "` WHERE `uuid` = ? AND `service` = ? AND `time` = ? AND `server` = ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, vote.getUuid());
ps.setString(2, vote.getService());
ps.setLong(3, vote.getTime());
ps.setString(4, server);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void removeVotesByServer(String server) {
String sql = "DELETE FROM `" + tableName + "` WHERE `server` = ?;";
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, server);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void clearTable() {
try {
new Query(mysql, "TRUNCATE TABLE `" + tableName + "`;").executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close() {
mysql.disconnect();
}
private List<VoteRow> selectVotes(String sql, Object[] params) {
List<VoteRow> list = new ArrayList<VoteRow>();
try (Connection conn = mysql.getConnectionManager().getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
VoteRow v = new VoteRow(rs.getInt("id"), rs.getString("uuid"), rs.getString("playerName"),
rs.getString("service"), rs.getLong("time"), rs.getBoolean("realVote"),
rs.getString("text"), rs.getString("server"));
list.add(v);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
public static class VoteRow {
private final int id;
private final String uuid;
private final String playerName;
private final String service;
private final long time;
private final boolean realVote;
private final String text;
private final String server;
public VoteRow(int id, String uuid, String playerName, String service, long time, boolean realVote, String text,
String server) {
this.id = id;
this.uuid = uuid;
this.playerName = playerName;
this.service = service;
this.time = time;
this.realVote = realVote;
this.text = text;
this.server = server;
}
public int getId() {
return id;
}
public String getUuid() {
return uuid;
}
public String getPlayerName() {
return playerName;
}
public String getService() {
return service;
}
public long getTime() {
return time;
}
public boolean isRealVote() {
return realVote;
}
public String getText() {
return text;
}
public String getServer() {
return server;
}
}
}
@@ -0,0 +1,473 @@
package com.bencodez.votingplugin.proxy.cache;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import com.bencodez.simpleapi.sql.mysql.MySQL;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
import com.bencodez.votingplugin.proxy.BungeeMessageData;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
import com.bencodez.votingplugin.timequeue.VoteTimeQueue;
import lombok.Getter;
public abstract class VoteCacheHandler {
@Getter
private Queue<VoteTimeQueue> timeChangeQueue = new ConcurrentLinkedQueue<>();
// uuid based
private ConcurrentHashMap<String, ArrayList<OfflineBungeeVote>> cachedOnlineVotes = new ConcurrentHashMap<>();
// server based
private ConcurrentHashMap<String, ArrayList<OfflineBungeeVote>> cachedVotes = new ConcurrentHashMap<>();
public boolean hasVotes(String server) {
return cachedVotes.containsKey(server);
}
public ArrayList<OfflineBungeeVote> getVotes(String server) {
return cachedVotes.getOrDefault(server, new ArrayList<>());
}
public void addServerVote(String server, OfflineBungeeVote vote) {
cachedVotes.putIfAbsent(server, new ArrayList<>());
cachedVotes.get(server).add(vote);
if (useMySQL) {
voteCacheTable.insertVote(vote.getUuid(), vote.getPlayerName(), vote.getService(), vote.getTime(),
vote.isRealVote(), vote.getText(), server);
} else {
jsonStorage.addVote(server, cachedVotes.get(server).size() - 1, vote);
jsonStorage.save();
}
}
public void removeVote(String server, String uuid) {
if (cachedVotes.containsKey(server)) {
ArrayList<OfflineBungeeVote> votes = cachedVotes.get(server);
votes.removeIf(vote -> vote.getUuid().equals(uuid));
if (useMySQL) {
voteCacheTable.removeVotesByServerAndUUID(server, uuid);
} else {
jsonStorage.removeServerVote(server, uuid);
jsonStorage.save();
}
}
}
public void removeVotes(String server) {
cachedVotes.remove(server);
if (useMySQL) {
voteCacheTable.removeVotesByServer(server);
} else {
jsonStorage.removeServerVotes(server);
jsonStorage.save();
}
}
public boolean hasOnlineVotes(String uuid) {
return cachedOnlineVotes.containsKey(uuid);
}
public ArrayList<OfflineBungeeVote> getOnlineVotes(String uuid) {
return cachedOnlineVotes.getOrDefault(uuid, new ArrayList<>());
}
public void addOnlineVote(String uuid, OfflineBungeeVote vote) {
cachedOnlineVotes.putIfAbsent(uuid, new ArrayList<>());
cachedOnlineVotes.get(uuid).add(vote);
if (useMySQL) {
onlineVoteCacheTable.insertOnlineVote(vote.getUuid(), vote.getPlayerName(), vote.getService(),
vote.getTime(), vote.isRealVote(), vote.getText());
} else {
jsonStorage.addVoteOnline(uuid, cachedOnlineVotes.get(uuid).size() - 1, vote);
jsonStorage.save();
}
}
public void removeOnlineVotes(String uuid) {
cachedOnlineVotes.remove(uuid);
if (useMySQL) {
onlineVoteCacheTable.removeVotesForUUID(uuid);
} else {
jsonStorage.removeOnlineVotes(uuid);
jsonStorage.save();
}
}
public void checkVoteCacheTime(int voteCacheTime) {
long cTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
// Collect expired online votes
ArrayList<OfflineBungeeVote> expiredOnlineVotes = new ArrayList<>();
for (Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedOnlineVotes.entrySet()) {
ArrayList<OfflineBungeeVote> votes = entry.getValue();
for (OfflineBungeeVote vote : votes) {
if (vote.getTime() + (voteCacheTime * 24 * 60 * 60 * 1000) < cTime) {
debug1("Removing vote from cache: " + vote.toString());
expiredOnlineVotes.add(vote);
}
}
}
removeOnlineVotes(expiredOnlineVotes);
// Collect expired server votes
ArrayList<OfflineBungeeVote> expiredServerVotes = new ArrayList<>();
for (Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedVotes.entrySet()) {
ArrayList<OfflineBungeeVote> votes = entry.getValue();
for (OfflineBungeeVote vote : votes) {
if (vote.getTime() + (voteCacheTime * 24 * 60 * 60 * 1000) < cTime) {
debug1("Removing vote from cache: " + vote.toString());
expiredServerVotes.add(vote);
}
}
}
for (String server : cachedVotes.keySet()) {
removeServerVotes(server, expiredServerVotes);
}
}
public void resetMilestoneCountInVotes() {
// Iterate through cached online votes
for (Map.Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedOnlineVotes.entrySet()) {
ArrayList<OfflineBungeeVote> votes = entry.getValue();
for (OfflineBungeeVote vote : votes) {
String updatedText = updateMilestoneCount(vote.getText());
vote.setText(updatedText);
}
}
// Iterate through cached votes
for (Map.Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedVotes.entrySet()) {
ArrayList<OfflineBungeeVote> votes = entry.getValue();
for (OfflineBungeeVote vote : votes) {
String updatedText = updateMilestoneCount(vote.getText());
vote.setText(updatedText);
}
}
}
private String updateMilestoneCount(String text) {
BungeeMessageData data = new BungeeMessageData(text);
data.setMilestoneCount(0); // Reset milestone count to 0
return data.toString();
}
public void saveVoteCache() {
if (useMySQL) {
if (!getTimeChangeQueue().isEmpty()) {
for (VoteTimeQueue vote : getTimeChangeQueue()) {
timedVoteCacheTable.insertTimedVote(vote.getName(), vote.getService(), vote.getTime());
}
}
} else {
if (!getTimeChangeQueue().isEmpty()) {
int num = 0;
for (VoteTimeQueue vote : getTimeChangeQueue()) {
jsonStorage.addTimedVote(num, vote);
num++;
}
}
jsonStorage.save();
}
}
public void addTimeVoteToCache(VoteTimeQueue vote) {
timeChangeQueue.add(vote);
}
public void load() {
if (useMySQL) {
// Load votes from MySQL
voteCacheTable.getAllVotes().forEach(voteRow -> {
OfflineBungeeVote vote = new OfflineBungeeVote(voteRow.getPlayerName(), voteRow.getUuid(),
voteRow.getService(), voteRow.getTime(), voteRow.isRealVote(), voteRow.getText());
String server = voteRow.getServer();
cachedVotes.putIfAbsent(server, new ArrayList<>());
cachedVotes.get(server).add(vote);
});
// Load online votes from MySQL
onlineVoteCacheTable.getAllVotes().forEach(voteRow -> {
OfflineBungeeVote vote = new OfflineBungeeVote(voteRow.getPlayerName(), voteRow.getUuid(),
voteRow.getService(), voteRow.getTime(), voteRow.isRealvote(), voteRow.getText());
String player = vote.getUuid();
cachedOnlineVotes.putIfAbsent(player, new ArrayList<>());
cachedOnlineVotes.get(player).add(vote);
});
// Load timed votes from MySQL
ArrayList<VoteTimeQueue> timedVotes = new ArrayList<>();
timedVoteCacheTable.getAllVotes().forEach(timedVoteRow -> {
VoteTimeQueue voteTimeQueue = new VoteTimeQueue(timedVoteRow.getPlayerName(), timedVoteRow.getService(),
timedVoteRow.getTime());
timedVotes.add(voteTimeQueue);
});
timeChangeQueue.addAll(timedVotes);
timedVoteCacheTable.clearTable();
} else {
try {
for (String key : jsonStorage.getTimedVoteCache()) {
DataNode data = jsonStorage.getTimedVoteCache(key);
if (data != null && data.isObject()) {
String name = data.has("Name") ? data.get("Name").asString() : "";
String service = data.has("Service") ? data.get("Service").asString() : "";
long time = data.has("Time") ? data.get("Time").asLong() : 0L;
getTimeChangeQueue().add(new VoteTimeQueue(name, service, time));
}
}
} catch (Exception e) {
e.printStackTrace();
}
try {
for (String server : jsonStorage.getServers()) {
ArrayList<OfflineBungeeVote> votes = new ArrayList<>();
for (String num : jsonStorage.getServerVotes(server)) {
DataNode data = jsonStorage.getServerVotes(server, num);
if (data != null && data.isObject()) {
String name = data.has("Name") ? data.get("Name").asString() : "";
String uuid = data.has("UUID") ? data.get("UUID").asString() : "";
String service = data.has("Service") ? data.get("Service").asString() : "";
long time = data.has("Time") ? data.get("Time").asLong() : 0L;
boolean real = data.has("Real") && data.get("Real").asBoolean();
String text = data.has("Text") ? data.get("Text").asString() : "";
votes.add(new OfflineBungeeVote(name, uuid, service, time, real, text));
}
}
cachedVotes.put(server, votes);
}
} catch (Exception e) {
e.printStackTrace();
}
try {
for (String player : jsonStorage.getPlayers()) {
ArrayList<OfflineBungeeVote> votes = new ArrayList<>();
for (String num : jsonStorage.getOnlineVotes(player)) {
DataNode data = jsonStorage.getOnlineVotes(player, num);
if (data != null && data.isObject()) {
String name = data.has("Name") ? data.get("Name").asString() : "";
String uuid = data.has("UUID") ? data.get("UUID").asString() : "";
String service = data.has("Service") ? data.get("Service").asString() : "";
long time = data.has("Time") ? data.get("Time").asLong() : 0L;
boolean real = data.has("Real") && data.get("Real").asBoolean();
String text = data.has("Text") ? data.get("Text").asString() : "";
votes.add(new OfflineBungeeVote(name, uuid, service, time, real, text));
}
}
cachedOnlineVotes.put(player, votes);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// log vote cache load summary
debug1("Loaded " + cachedVotes.size() + " server vote caches.");
int totalServerVotes = cachedVotes.values().stream().mapToInt(ArrayList::size).sum();
debug1("Loaded " + totalServerVotes + " total server votes.");
debug1("Loaded " + cachedOnlineVotes.size() + " online vote caches.");
int totalOnlineVotes = cachedOnlineVotes.values().stream().mapToInt(ArrayList::size).sum();
debug1("Loaded " + totalOnlineVotes + " total online votes.");
debug1("Loaded " + timeChangeQueue.size() + " timed votes.");
}
private final boolean useMySQL;
private ProxyVoteCacheTable voteCacheTable;
private ProxyTimedVoteCacheTable timedVoteCacheTable;
private ProxyOnlineVoteCacheTable onlineVoteCacheTable;
private IVoteCache jsonStorage;
public abstract void logInfo1(String msg);
public abstract void logSevere1(String msg);
public abstract void debug1(Exception e);
public abstract void debug1(String msg);
public VoteCacheHandler(MysqlConfig mysqlConfig, boolean useMySQL, boolean useExistingConnection, MySQL mysql,
boolean debug, IVoteCache jsonStorage) {
this.useMySQL = useMySQL;
if (useMySQL) {
if (useExistingConnection) {
voteCacheTable = new ProxyVoteCacheTable(mysql, mysqlConfig.getTablePrefix(), debug) {
@Override
public void logSevere(String string) {
logSevere1(string);
}
@Override
public void logInfo(String string) {
logInfo1(string);
}
@Override
public void debug(Exception e) {
if (debug)
debug1(e);
}
};
timedVoteCacheTable = new ProxyTimedVoteCacheTable(mysql, mysqlConfig.getTablePrefix(), debug) {
@Override
public void logSevere(String string) {
logSevere1(string);
}
@Override
public void logInfo(String string) {
logInfo1(string);
}
@Override
public void debug(Exception e) {
if (debug)
debug1(e);
}
};
onlineVoteCacheTable = new ProxyOnlineVoteCacheTable(mysql, mysqlConfig.getTablePrefix(), debug) {
@Override
public void logSevere(String string) {
logSevere1(string);
}
@Override
public void logInfo(String string) {
logInfo1(string);
}
@Override
public void debug(Exception e) {
if (debug)
debug1(e);
}
};
} else {
voteCacheTable = new ProxyVoteCacheTable(mysqlConfig, debug) {
@Override
public void logSevere(String string) {
logSevere1(string);
}
@Override
public void logInfo(String string) {
logInfo1(string);
}
@Override
public void debug(Exception e) {
if (debug)
debug1(e);
}
};
timedVoteCacheTable = new ProxyTimedVoteCacheTable(voteCacheTable.getMysql(),
mysqlConfig.getTablePrefix(), debug) {
@Override
public void logSevere(String string) {
logSevere1(string);
}
@Override
public void logInfo(String string) {
logInfo1(string);
}
@Override
public void debug(Exception e) {
if (debug)
debug1(e);
}
};
onlineVoteCacheTable = new ProxyOnlineVoteCacheTable(voteCacheTable.getMysql(),
mysqlConfig.getTablePrefix(), debug) {
@Override
public void logSevere(String string) {
logSevere1(string);
}
@Override
public void logInfo(String string) {
logInfo1(string);
}
@Override
public void debug(Exception e) {
if (debug)
debug1(e);
}
};
}
} else {
this.jsonStorage = jsonStorage;
}
}
public String[] getCachedVotesServers() {
return cachedVotes.keySet().toArray(new String[0]);
}
public void removeServerVotes(String server, ArrayList<OfflineBungeeVote> removed) {
for (OfflineBungeeVote vote : removed) {
for (Map.Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedVotes.entrySet()) {
if (entry.getKey().equals(server)) {
entry.getValue().removeIf(v -> v.getUuid().equals(vote.getUuid())
&& v.getService().equals(vote.getService()) && v.getTime() == vote.getTime());
}
}
if (useMySQL) {
voteCacheTable.removeVote(vote, server);
} else {
jsonStorage.removeVote(server, vote);
jsonStorage.save();
}
}
}
public void removeOnlineVotes(ArrayList<OfflineBungeeVote> removed) {
for (OfflineBungeeVote vote : removed) {
for (Map.Entry<String, ArrayList<OfflineBungeeVote>> entry : cachedOnlineVotes.entrySet()) {
entry.getValue().removeIf(v -> v.getUuid().equals(vote.getUuid())
&& v.getService().equals(vote.getService()) && v.getTime() == vote.getTime());
}
if (useMySQL) {
onlineVoteCacheTable.removeOnlineVote(vote);
} else {
jsonStorage.removeOnlineVote(vote);
jsonStorage.save();
}
}
}
}
@@ -400,4 +400,19 @@ public class Config extends VelocityYMLFile implements VotingPluginProxyConfig {
return getBoolean(getNode("ResetMilestonesMonthly"), false);
}
@Override
public boolean getVoteCacheUseMySQL() {
return getBoolean(getNode("VoteCache", "UseMySQL"), false);
}
@Override
public boolean getVoteCacheUseMainMySQL() {
return getBoolean(getNode("VoteCache", "UseMainMySQL"), true);
}
@Override
public int getTimeWeekOffSet() {
return getInt(getNode("TimeWeekOffSet"), 0);
}
}
@@ -5,42 +5,45 @@ import java.util.Collection;
import com.bencodez.simpleapi.file.velocity.VelocityJSONFile;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
import com.bencodez.votingplugin.proxy.cache.ConfigDataNode;
import com.bencodez.votingplugin.proxy.cache.DataNode;
import com.bencodez.votingplugin.proxy.cache.IVoteCache;
import com.bencodez.votingplugin.timequeue.VoteTimeQueue;
import ninja.leaping.configurate.ConfigurationNode;
public class VoteCache extends VelocityJSONFile {
public class VoteCache extends VelocityJSONFile implements IVoteCache {
public VoteCache(File file) {
super(file);
}
@Override
public void addTimedVote(int num, VoteTimeQueue voteTimedQueue) {
getNode("TimedVoteCache", "" + num, "Name").setValue(voteTimedQueue.getName());
getNode("TimedVoteCache", "" + num, "Service").setValue(voteTimedQueue.getService());
getNode("TimedVoteCache", "" + num, "Time").setValue(voteTimedQueue.getTime());
getNode("TimedVoteCache", String.valueOf(num), "Name").setValue(voteTimedQueue.getName());
getNode("TimedVoteCache", String.valueOf(num), "Service").setValue(voteTimedQueue.getService());
getNode("TimedVoteCache", String.valueOf(num), "Time").setValue(voteTimedQueue.getTime());
}
@Override
public void addVote(String server, int num, OfflineBungeeVote voteData) {
getNode("VoteCache", server, "" + num, "Name").setValue(voteData.getPlayerName());
getNode("VoteCache", server, "" + num, "Service").setValue(voteData.getService());
getNode("VoteCache", server, "" + num, "UUID").setValue(voteData.getUuid());
getNode("VoteCache", server, "" + num, "Time").setValue(voteData.getTime());
getNode("VoteCache", server, "" + num, "Real").setValue(voteData.isRealVote());
getNode("VoteCache", server, "" + num, "Text").setValue(voteData.getText());
getNode("VoteCache", server, String.valueOf(num), "Name").setValue(voteData.getPlayerName());
getNode("VoteCache", server, String.valueOf(num), "Service").setValue(voteData.getService());
getNode("VoteCache", server, String.valueOf(num), "UUID").setValue(voteData.getUuid());
getNode("VoteCache", server, String.valueOf(num), "Time").setValue(voteData.getTime());
getNode("VoteCache", server, String.valueOf(num), "Real").setValue(voteData.isRealVote());
getNode("VoteCache", server, String.valueOf(num), "Text").setValue(voteData.getText());
}
@Override
public void addVoteOnline(String player, int num, OfflineBungeeVote voteData) {
getNode("OnlineCache", player, "" + num, "Name").setValue(voteData.getPlayerName());
getNode("OnlineCache", player, "" + num, "Service").setValue(voteData.getService());
getNode("OnlineCache", player, "" + num, "UUID").setValue(voteData.getUuid());
getNode("OnlineCache", player, "" + num, "Time").setValue(voteData.getTime());
getNode("OnlineCache", player, "" + num, "Real").setValue(voteData.isRealVote());
getNode("OnlineCache", player, "" + num, "Text").setValue(voteData.getText());
getNode("OnlineCache", player, String.valueOf(num), "Name").setValue(voteData.getPlayerName());
getNode("OnlineCache", player, String.valueOf(num), "Service").setValue(voteData.getService());
getNode("OnlineCache", player, String.valueOf(num), "UUID").setValue(voteData.getUuid());
getNode("OnlineCache", player, String.valueOf(num), "Time").setValue(voteData.getTime());
getNode("OnlineCache", player, String.valueOf(num), "Real").setValue(voteData.isRealVote());
getNode("OnlineCache", player, String.valueOf(num), "Text").setValue(voteData.getText());
}
@Override
public void clearData() {
getNode("VoteCache").setValue(null);
getNode("OnlineCache").setValue(null);
@@ -48,60 +51,169 @@ public class VoteCache extends VelocityJSONFile {
save();
}
@Override
public Collection<String> getOnlineVotes(String name) {
return getKeys(getNode("OnlineCache", name));
}
public ConfigurationNode getOnlineVotes(String name, String num) {
return getNode("OnlineCache", name, num);
@Override
public ConfigDataNode getOnlineVotes(String name, String num) {
return new ConfigDataNode(getNode("OnlineCache", name, num));
}
@Override
public Collection<String> getPlayers() {
return getKeys(getNode("OnlineCache"));
}
@Override
public Collection<String> getServers() {
return getKeys(getNode("VoteCache"));
}
@Override
public Collection<String> getServerVotes(String server) {
return getKeys(getNode("VoteCache", server));
}
public ConfigurationNode getServerVotes(String server, String num) {
return getNode("VoteCache", server, num);
@Override
public ConfigDataNode getServerVotes(String server, String num) {
return new ConfigDataNode(getNode("VoteCache", server, num));
}
@Override
public Collection<String> getTimedVoteCache() {
return getKeys(getNode("TimedVoteCache"));
}
public ConfigurationNode getTimedVoteCache(String name) {
return getNode("TimedVoteCache", name);
@Override
public ConfigDataNode getTimedVoteCache(String key) {
return new ConfigDataNode(getNode("TimedVoteCache", key));
}
@Override
public int getVotePartyCache(String server) {
return getNode("VoteParty").getNode("Cache").getNode(server).getInt(0);
return getNode("VoteParty", "Cache", server).getInt(0);
}
@Override
public int getVotePartyCurrentVotes() {
return getInt(getNode("VoteParty").getNode("CurrentVotes"), 0);
return getInt(getNode("VoteParty", "CurrentVotes"), 0);
}
@Override
public int getVotePartyInreaseVotesRequired() {
return getInt(getNode("VoteParty").getNode("IncreaseVotes"), 0);
return getInt(getNode("VoteParty", "IncreaseVotes"), 0);
}
@Override
public void setVotePartyCache(String server, int amount) {
getNode("VoteParty").getNode("Cache").getNode(server).setValue(amount);
getNode("VoteParty", "Cache", server).setValue(amount);
}
@Override
public void setVotePartyCurrentVotes(int amount) {
getNode("VoteParty").getNode("CurrentVotes").setValue(amount);
getNode("VoteParty", "CurrentVotes").setValue(amount);
}
@Override
public void setVotePartyInreaseVotesRequired(int amount) {
getNode("VoteParty").getNode("IncreaseVotes").setValue(amount);
getNode("VoteParty", "IncreaseVotes").setValue(amount);
}
@Override
public void removeOnlineVotes(String player) {
getNode("OnlineCache", player).setValue(null);
}
@Override
public void removeServerVotes(String server) {
getNode("VoteCache", server).setValue(null);
}
@Override
public void removeServerVote(String server, String uuid) {
Collection<String> votes = getServerVotes(server);
if (votes == null) {
return;
}
// search for vote with uuid and remove it
for (String num : votes) {
ConfigDataNode node = getServerVotes(server, num);
if (node == null) {
continue;
}
DataNode uuidNode = node.get("UUID");
if (uuidNode == null) {
continue;
}
String nodeUuid = uuidNode.asString();
if (nodeUuid != null && nodeUuid.equals(uuid)) {
getNode("VoteCache", server, num).setValue(null);
}
}
}
@Override
public void removeVote(String server, OfflineBungeeVote vote) {
Collection<String> votes = getServerVotes(server);
if (votes == null) {
return;
}
for (String num : votes) {
ConfigDataNode node = getServerVotes(server, num);
if (node == null) {
continue;
}
DataNode uuidNode = node.get("UUID");
DataNode serviceNode = node.get("Service");
DataNode timeNode = node.get("Time");
if (uuidNode == null || serviceNode == null || timeNode == null) {
continue;
}
String nodeUuid = uuidNode.asString();
String nodeService = serviceNode.asString();
Long nodeTime = timeNode.asLong();
if (nodeUuid != null && nodeService != null && nodeTime != null && nodeUuid.equals(vote.getUuid())
&& nodeService.equals(vote.getService()) && nodeTime.longValue() == vote.getTime()) {
getNode("VoteCache", server, num).setValue(null);
}
}
}
@Override
public void removeOnlineVote(OfflineBungeeVote vote) {
Collection<String> players = getPlayers();
if (players == null) {
return;
}
for (String player : players) {
Collection<String> onlineVotes = getOnlineVotes(player);
if (onlineVotes == null) {
continue;
}
for (String num : onlineVotes) {
ConfigDataNode node = getOnlineVotes(player, num);
if (node == null) {
continue;
}
DataNode uuidNode = node.get("UUID");
DataNode serviceNode = node.get("Service");
DataNode timeNode = node.get("Time");
if (uuidNode == null || serviceNode == null || timeNode == null) {
continue;
}
String nodeUuid = uuidNode.asString();
String nodeService = serviceNode.asString();
Long nodeTime = timeNode.asLong();
if (nodeUuid != null && nodeService != null && nodeTime != null && nodeUuid.equals(vote.getUuid())
&& nodeService.equals(vote.getService()) && nodeTime.longValue() == vote.getTime()) {
getNode("OnlineCache", player, num).setValue(null);
}
}
}
}
}
@@ -35,14 +35,12 @@ import com.bencodez.advancedcore.bungeeapi.globaldata.GlobalDataHandlerProxy;
import com.bencodez.advancedcore.bungeeapi.globaldata.GlobalMySQL;
import com.bencodez.simpleapi.file.velocity.VelocityYMLFile;
import com.bencodez.simpleapi.sql.DataType;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfigVelocity;
import com.bencodez.votingplugin.proxy.BungeeMethod;
import com.bencodez.votingplugin.proxy.BungeeVersion;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
import com.bencodez.votingplugin.proxy.ProxyMysqlUserTable;
import com.bencodez.votingplugin.proxy.VotingPluginProxy;
import com.bencodez.votingplugin.proxy.VotingPluginProxyConfig;
import com.bencodez.votingplugin.timequeue.VoteTimeQueue;
import com.bencodez.votingplugin.topvoter.TopVoter;
import com.google.inject.Inject;
import com.velocitypowered.api.command.CommandMeta;
@@ -268,6 +266,12 @@ public class VotingPluginVelocity {
if (type.equals(TimeType.MONTH)) {
getVotingPluginProxy().getProxyMySQL().copyColumnData(TopVoter.Monthly.getColumnName(),
"LastMonthTotal");
if (getVotingPluginProxy().getConfig().getResetMilestonesMonthly()) {
getVotingPluginProxy().getProxyMySQL().wipeColumnData("MilestoneCount",
DataType.INTEGER);
getVotingPluginProxy().getProxyMySQL().wipeColumnData("GottenMileStones",
DataType.STRING);
}
}
getVotingPluginProxy().getProxyMySQL().wipeColumnData(TopVoter.of(type).getColumnName(),
DataType.INTEGER);
@@ -328,6 +332,12 @@ public class VotingPluginVelocity {
if (type.equals(TimeType.MONTH)) {
getVotingPluginProxy().getProxyMySQL().copyColumnData(TopVoter.Monthly.getColumnName(),
"LastMonthTotal");
if (getVotingPluginProxy().getConfig().getResetMilestonesMonthly()) {
getVotingPluginProxy().getProxyMySQL().wipeColumnData("MilestoneCount",
DataType.INTEGER);
getVotingPluginProxy().getProxyMySQL().wipeColumnData("GottenMileStones",
DataType.STRING);
}
}
getVotingPluginProxy().getProxyMySQL().wipeColumnData(TopVoter.of(type).getColumnName(),
DataType.INTEGER);
@@ -417,35 +427,6 @@ public class VotingPluginVelocity {
@Subscribe
public void onProxyDisable(ProxyShutdownEvent event) {
if (votingPluginProxy.getMethod().equals(BungeeMethod.PLUGINMESSAGING)) {
logger.info("VotingPlugin saving vote cache: " + getVotingPluginProxy().getCachedVotes().size() + "/"
+ getVotingPluginProxy().getCachedOnlineVotes().size());
for (Entry<String, ArrayList<OfflineBungeeVote>> entry : getVotingPluginProxy().getCachedVotes()
.entrySet()) {
String server = entry.getKey();
int num = 0;
for (OfflineBungeeVote voteData : entry.getValue()) {
voteCacheFile.addVote(server, num, voteData);
num++;
}
}
for (Entry<String, ArrayList<OfflineBungeeVote>> entry : getVotingPluginProxy().getCachedOnlineVotes()
.entrySet()) {
String name = entry.getKey();
int num = 0;
for (OfflineBungeeVote voteData : entry.getValue()) {
voteCacheFile.addVoteOnline(name, num, voteData);
num++;
}
}
}
if (!getVotingPluginProxy().getTimeChangeQueue().isEmpty()) {
int num = 0;
for (VoteTimeQueue vote : getVotingPluginProxy().getTimeChangeQueue()) {
voteCacheFile.addTimedVote(num, vote);
num++;
}
}
voteCacheFile.save();
nonVotedPlayersCache.save();
@@ -749,6 +730,11 @@ public class VotingPluginVelocity {
return timer;
}
@Override
public MysqlConfig getVoteCacheMySQLConfig() {
return new MysqlConfigVelocity("VoteCache", config);
}
};
try {
@@ -808,66 +794,20 @@ public class VotingPluginVelocity {
voteCacheFile.save();
}
getVotingPluginProxy().load();
try {
for (String key : voteCacheFile.getTimedVoteCache()) {
ConfigurationNode data = voteCacheFile.getTimedVoteCache(key);
getVotingPluginProxy().getTimeChangeQueue().add(new VoteTimeQueue(data.getNode("Name").getString(),
data.getNode("Service").getString(), data.getNode("Time").getLong()));
}
getVotingPluginProxy().processQueue();
} catch (Exception e) {
e.printStackTrace();
}
try {
for (String server : voteCacheFile.getServers()) {
ArrayList<OfflineBungeeVote> vote = new ArrayList<>();
for (String num : voteCacheFile.getServerVotes(server)) {
ConfigurationNode data = voteCacheFile.getServerVotes(server, num);
vote.add(new OfflineBungeeVote(data.getNode("Name").getString(),
data.getNode("UUID").getString(), data.getNode("Service").getString(),
data.getNode("Time").getLong(), data.getNode("Real").getBoolean(),
data.getNode("Text").getString()));
}
getVotingPluginProxy().getCachedVotes().put(server, vote);
}
} catch (Exception e) {
e.printStackTrace();
}
try {
for (String player : voteCacheFile.getPlayers()) {
ArrayList<OfflineBungeeVote> vote = new ArrayList<>();
for (String num : voteCacheFile.getOnlineVotes(player)) {
ConfigurationNode data = voteCacheFile.getOnlineVotes(player, num);
vote.add(new OfflineBungeeVote(data.getNode("Name").getString(),
data.getNode("UUID").getString(), data.getNode("Service").getString(),
data.getNode("Time").getLong(), data.getNode("Real").getBoolean(),
data.getNode("Text").getString()));
}
getVotingPluginProxy().getCachedOnlineVotes().put(player, vote);
}
} catch (Exception e) {
e.printStackTrace();
}
voteCacheFile.clearData();
getVotingPluginProxy().load(voteCacheFile);
voteCheckTask = server.getScheduler().buildTask(this, () -> {
if (getVotingPluginProxy().getGlobalDataHandler() == null
|| !getVotingPluginProxy().getGlobalDataHandler().isTimeChangedHappened()) {
for (String server : getVotingPluginProxy().getCachedVotes().keySet()) {
for (String server : getVotingPluginProxy().getVoteCacheHandler().getCachedVotesServers()) {
getVotingPluginProxy().checkCachedVotes(server);
}
for (String player : getVotingPluginProxy().getCachedOnlineVotes().keySet()) {
if (server.getPlayer(UUID.fromString(player)).isPresent()) {
getVotingPluginProxy().checkOnlineVotes(
server.getPlayer(UUID.fromString(player)).get().getUsername(), player, null);
}
for (Player player : server.getAllPlayers()) {
getVotingPluginProxy().checkOnlineVotes(player.getUsername(), player.getUniqueId().toString(),
null);
}
}
}).delay(120, TimeUnit.SECONDS).repeat(60, TimeUnit.SECONDS).schedule();
+42 -22
View File
@@ -15,26 +15,45 @@
DataStorage: SQLITE
# Information for mysql
# See the end of the config for cache options
MySQL:
Host: ''
# Default port is 3306
Port: 3306
Port: 3306
Database: ''
Username: ''
Password: ''
# Max number of connections
# Max number of connections in the pool
MaxConnections: 1
# Table name prefix, for use with mutliple servers
# Don't use with PLUGINMESSAGING bungee setup
# Optional explicit table name override (leave empty to let the plugin choose)
#Name: ''
# Table name prefix (useful when multiple servers share one DB)
# Don't use with proxy setups
Prefix: ''
#UseSSL: true
#PublicKeyRetrieval: false
#MaxLifeTime: -1
#Additional info on connection string
# --- Driver / security options ---
# UseSSL: false
# PublicKeyRetrieval: false # Needed on some MySQL setups with caching_sha2_password
# UseMariaDB: false # Attempt to use MariaDB driver instead of MySQL
# --- Connection / lifecycle tuning ---
# MaxLifeTime: -1 # ms; <=0 uses plugin's safe default (e.g., 25m)
# MinimumIdle: 2 # keep a small warm pool (2 is a good starting point)
# IdleTimeoutMs: 600000 # 10 minutes; retire idle connections before server kills them
# KeepaliveMs: 300000 # 5 minutes; lightweight ping to prevent NAT/idle drops
# ValidationMs: 5000 # 5 seconds; timeout for connection validation
# LeakDetectMs: 20000 # 20 seconds; logs if a connection is held too long (0 disables)
# ConnectionTimeout: 50000 # 50 seconds; wait for a free connection before failing
# --- Additional JDBC parameters appended to the URL ---
# Example: "&connectionCollation=utf8mb4_unicode_ci"
#Line: ''
#Attempt to use mariadb driver
#UseMariaDB: false
# Optional pool name (useful if you run multiple DB pools)
# PoolName: 'VotingPlugin-Hikari'
###########################################
@@ -618,23 +637,23 @@ UseMonthDateTotalsAsPrimaryTotal: false
# DiscordSRV settings
###########################################
DiscordSRV:
DiscordSRV:
# Enable DiscordSRV support
# Also enables using rewards in discord
# Also enables using rewards in discord
Enabled: false
# Show live top voter counts in discord
TopVoter:
AllTime:
AllTime:
# If true, will send top voter messages to discord
# Will send a message show top 10 voters
Enabled: false
# DiscordSRV channel to send messages to
# Will send a message show top 10 voters
Enabled: false
# DiscordSRV channel to send messages to
Channel: 1234565 # Replace with your channel ID
Title: 'All Time Top Voters'
RankDisplay: '%rank%: &6%player% - %votes% Votes'
RankDisplay: '%rank%: %player% - %votes% Votes'
Monthly:
# If true, will send top voter messages to discord
@@ -645,7 +664,7 @@ DiscordSRV:
Channel: 1234565 # Replace with your channel ID
Title: 'Top Voters of the Month'
RankDisplay: '%rank%: &6%player% - %votes% Votes'
RankDisplay: '%rank%: %player% - %votes% Votes'
Weekly:
# If true, will send top voter messages to discord
@@ -656,7 +675,7 @@ DiscordSRV:
Channel: 1234565 # Replace with your channel ID
Title: 'Top Voters of the Week'
RankDisplay: '%rank%: &6%player% - %votes% Votes'
RankDisplay: '%rank%: %player% - %votes% Votes'
Daily:
# If true, will send top voter messages to discord
@@ -667,7 +686,7 @@ DiscordSRV:
Channel: 1234565 # Replace with your channel ID
Title: 'Top Voters of the Day'
RankDisplay: '%rank%: &6%player% - %votes% Votes'
RankDisplay: '%rank%: %player% - %votes% Votes'
@@ -869,3 +888,4 @@ RequestAPI:
DefaultMethod: 'Anvil'
DisabledMethods: []
@@ -3,21 +3,45 @@
#
# MYSQL Required on all setups
# MYSQL INFO --------------------------
# MYSQL INFO --------------------------
Host: ''
# Default port is 3306
Port: 3306
Port: 3306
Database: ''
Username: ''
Password: ''
# Max number of connections
# Max number of connections in the pool
MaxConnections: 1
# Table name prefix, for use with mutliple servers
# Don't use with PLUGINMESSAGING bungee setup
# Optional explicit table name override (leave empty to let the plugin choose)
# Name: ''
# Table name prefix, for use with multiple servers
# Don't use with proxy setups unless you know what you're doing
Prefix: ''
#UseSSL: true
#PublicKeyRetrieval: false
#Attempt to use mariadb driver
#UseMariaDB: false
# --- Driver / security options ---
# UseSSL: false
# PublicKeyRetrieval: false # Needed on some MySQL setups with caching_sha2_password
# UseMariaDB: false # Attempt to use MariaDB driver instead of MySQL
# --- Connection / lifecycle tuning ---
# MaxLifeTime: -1 # ms; <=0 uses plugin's safe default (e.g., 25m)
# MinimumIdle: 2 # keep a small warm pool (2 is a good starting point)
# IdleTimeoutMs: 600000 # 10 minutes; retire idle connections before server kills them
# KeepaliveMs: 300000 # 5 minutes; lightweight ping to prevent NAT/idle drops
# ValidationMs: 5000 # 5 seconds; timeout for connection validation
# LeakDetectMs: 20000 # 20 seconds; logs if a connection is held too long (0 disables)
# ConnectionTimeout: 50000 # 50 seconds; wait for a free connection before failing
# --- Additional JDBC parameters appended to the URL ---
# Example: "&connectionCollation=utf8mb4_unicode_ci"
# Line: ''
# Optional pool name (useful if you run multiple DB pools)
# PoolName: 'VotingPlugin-Hikari'
####################################################################################
@@ -54,7 +78,14 @@ GlobalData:
#PublicKeyRetrieval: false
#Attempt to use mariadb driver
#UseMariaDB: false
####################################################################################
VoteCache:
# If enabled, will cache votes in mysql
UseMySQL: false
UseMainMySQL: true
# Mysql settings only used if above is false
####################################################################################
@@ -9,6 +9,7 @@ import java.util.concurrent.ScheduledExecutorService;
import org.mockito.Mockito;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
import com.bencodez.votingplugin.proxy.VotingPluginProxy;
import com.bencodez.votingplugin.proxy.VotingPluginProxyConfig;
@@ -205,4 +206,10 @@ public class VotingPluginProxyTestImpl extends VotingPluginProxy {
public ScheduledExecutorService getScheduler() {
return null;
}
@Override
public MysqlConfig getVoteCacheMySQLConfig() {
// TODO Auto-generated method stub
return null;
}
}
@@ -1,103 +0,0 @@
package com.bencodez.votingplugin.tests;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.bencodez.votingplugin.proxy.OfflineBungeeVote;
import com.bencodez.votingplugin.proxy.VotingPluginProxy;
import com.bencodez.votingplugin.proxy.VotingPluginProxyConfig;
public class VotingPluginProxyVoteCacheTest {
private VotingPluginProxy proxy;
private VotingPluginProxyConfig config;
private ConcurrentHashMap<String, ArrayList<OfflineBungeeVote>> cachedOnlineVotes;
private ConcurrentHashMap<String, ArrayList<OfflineBungeeVote>> cachedVotes;
@BeforeEach
public void setUp() {
proxy = mock(VotingPluginProxy.class, CALLS_REAL_METHODS);
config = mock(VotingPluginProxyConfig.class);
when(proxy.getConfig()).thenReturn(config);
cachedOnlineVotes = new ConcurrentHashMap<>();
cachedVotes = new ConcurrentHashMap<>();
proxy.setCachedOnlineVotes(cachedOnlineVotes);
proxy.setCachedVotes(cachedVotes);
}
@Test
void checkVoteCacheTime_removesExpiredVotesFromCachedOnlineVotes() {
when(config.getVoteCacheTime()).thenReturn(1); // 1 day
long currentTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long expiredTime = currentTime - (2 * 24 * 60 * 60 * 1000); // 2 days ago
ArrayList<OfflineBungeeVote> votes = new ArrayList<>();
votes.add(new OfflineBungeeVote("player1", "uuid1", "service1", expiredTime, true, "text1"));
cachedOnlineVotes.put("server1", votes);
proxy.checkVoteCacheTime();
assertTrue(cachedOnlineVotes.get("server1").isEmpty());
}
@Test
void checkVoteCacheTime_doesNotRemoveNonExpiredVotesFromCachedOnlineVotes() {
when(config.getVoteCacheTime()).thenReturn(1); // 1 day
long currentTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long validTime = currentTime - (12 * 60 * 60 * 1000); // 12 hours ago
ArrayList<OfflineBungeeVote> votes = new ArrayList<>();
votes.add(new OfflineBungeeVote("player1", "uuid1", "service1", validTime, true, "text1"));
cachedOnlineVotes.put("server1", votes);
proxy.checkVoteCacheTime();
assertFalse(cachedOnlineVotes.get("server1").isEmpty());
}
@Test
void checkVoteCacheTime_removesExpiredVotesFromCachedVotes() {
when(config.getVoteCacheTime()).thenReturn(1); // 1 day
long currentTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long expiredTime = currentTime - (2 * 24 * 60 * 60 * 1000); // 2 days ago
ArrayList<OfflineBungeeVote> votes = new ArrayList<>();
votes.add(new OfflineBungeeVote("player1", "uuid1", "service1", expiredTime, true, "text1"));
cachedVotes.put("server1", votes);
proxy.checkVoteCacheTime();
assertTrue(cachedVotes.get("server1").isEmpty());
}
@Test
void checkVoteCacheTime_doesNotRemoveNonExpiredVotesFromCachedVotes() {
when(config.getVoteCacheTime()).thenReturn(1); // 1 day
long currentTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long validTime = currentTime - (12 * 60 * 60 * 1000); // 12 hours ago
ArrayList<OfflineBungeeVote> votes = new ArrayList<>();
votes.add(new OfflineBungeeVote("player1", "uuid1", "service1", validTime, true, "text1"));
cachedVotes.put("server1", votes);
proxy.checkVoteCacheTime();
assertFalse(cachedVotes.get("server1").isEmpty());
}
}