Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ public void onReceive(JsonEnvelope msg) {
}
});

globalMessageHandler.addListener(new GlobalMessageListener(VotingPluginWire.SUB_VOTE_DELAY_REJECTED) {
@Override
public void onReceive(JsonEnvelope msg) {
handleWireVoteDelayRejected(msg);
}
});
globalMessageHandler.addListener(new GlobalMessageListener(VotingPluginWire.SUB_VOTE_UPDATE) {
@Override
public void onReceive(JsonEnvelope msg) {
Expand Down Expand Up @@ -557,6 +563,43 @@ private static long readLongSafe(String v, long def) {
}
}

private void handleWireVoteDelayRejected(JsonEnvelope msg) {
if (msg.getSchema() != VotingPluginWire.SCHEMA_VERSION) {
plugin.getLogger().warning("Incompatible version with bungee/proxy, please update all servers: "
+ msg.getSchema() + " != " + VotingPluginWire.SCHEMA_VERSION);
return;
}

if (!plugin.getOptions().isProcessRewards()) {
return;
}

VotingPluginWire.VoteDelayRejected rejected = VotingPluginWire.readVoteDelayRejected(msg);
if (rejected.uuid.isEmpty() || rejected.service.isEmpty()) {
return;
}

UUID javaUuid;
try {
javaUuid = UUID.fromString(rejected.uuid);
} catch (IllegalArgumentException e) {
plugin.getLogger().warning("Invalid UUID in VoteDelayRejected: " + rejected.uuid);
return;
}

VoteSite voteSite = plugin.getVoteSiteManager()
.getVoteSite(plugin.getVoteSiteManager().getVoteSiteName(true, rejected.service), true);
if (voteSite == null) {
plugin.getLogger().warning("No voting site with the service site: '" + rejected.service + "'");
return;
}

VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(javaUuid, rejected.player);
user.cache();
user.updateName(true);
voteSite.giveWaitUntilVoteDelayRewards(user, rejected.wasOnline && user.isOnline(), true);
}

/**
* Wire vote handler (Vote + VoteOnline).
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,29 @@ public void setData(String path, Object value) {
}
});

addDirectlyDefinedRewards(new DirectlyDefinedReward("VoteSites." + site.getKey() + ".WaitUntilVoteDelayRewards") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register delay rewards for dynamically generated sites

This bulk registration covers sites present during loadDirectlyDefined(), but ConfigVoteSites.generateVoteSite() dynamically reloads sites and then manually registers only Rewards and CoolDownEndRewards. Consequently, an auto-created site has no directly defined WaitUntilVoteDelayRewards entry until a full plugin reload, so reward-editor or migration lookups cannot access the new section immediately; add the new path to the dynamic registration flow as well.

Useful? React with 👍 / 👎.


@Override
public void createSection(String key) {
getConfigVoteSites().createSection(key);
}

@Override
public ConfigurationSection getFileData() {
return getConfigVoteSites().getData();
}

@Override
public void save() {
getConfigVoteSites().saveData();
}

@Override
public void setData(String path, Object value) {
getConfigVoteSites().setValue(path, value);
}
});

addDirectlyDefinedRewards(new DirectlyDefinedReward("VoteSites." + site.getKey() + ".CoolDownEndRewards") {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public void generateVoteSite(String siteName) {
set(siteName, "DisplayItem.Material", "STONE");
set(siteName, "DisplayItem.Amount", 1);
set(siteName, "Rewards.Messages.Player", "&aThanks for voting on %ServiceSite%!");
set(siteName, "WaitUntilVoteDelayRewards", Collections.emptyMap());

plugin.loadVoteSites();

Expand All @@ -86,6 +87,30 @@ public void setData(String path, Object value) {
}
});

plugin.addDirectlyDefinedRewards(
new DirectlyDefinedReward("VoteSites." + siteName + ".WaitUntilVoteDelayRewards") {

@Override
public void createSection(String key) {
plugin.getConfigVoteSites().createSection(key);
}

@Override
public ConfigurationSection getFileData() {
return plugin.getConfigVoteSites().getData();
}

@Override
public void save() {
plugin.getConfigVoteSites().saveData();
}

@Override
public void setData(String path, Object value) {
plugin.getConfigVoteSites().setValue(path, value);
}
});

plugin.addDirectlyDefinedRewards(
new DirectlyDefinedReward("VoteSites." + siteName + ".CoolDownEndRewards") {

Expand Down Expand Up @@ -194,6 +219,16 @@ public String getRewardsPath(String siteName) {
return "VoteSites." + siteName + ".Rewards";
}

/**
* Gets the rewards path used when a vote is rejected by WaitUntilVoteDelay.
*
* @param siteName the site name
* @return the wait-until-vote-delay rewards path
*/
public String getWaitUntilVoteDelayRewardsPath(String siteName) {
return "VoteSites." + siteName + ".WaitUntilVoteDelayRewards";
}

/**
* Gets the service site.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ public void onplayerVote(PlayerVoteEvent event) {
} else {
if (!user.hasPermission("VotingPlugin.BypassWaitUntilVoteDelay")) {
plugin.getLogger().info(user.getPlayerName() + " must wait until votedelay is over, ignoring vote");
boolean online = user.isOnline();
if (event.isBungee()) {
online = event.isWasOnline();
}
if (plugin.getOptions().isProcessRewards()) {
voteSite.giveWaitUntilVoteDelayRewards(user, online, event.isBungee());
Comment on lines +160 to +161

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve proxy dispatch when local rewards are disabled

When a proxy-delivered vote reaches a backend with ProcessRewards: false, this guard suppresses the delay reward entirely. The established ordinary-vote path explicitly continues for event.isBungee() at lines 219–220 and then uses server-mode reward dispatch, so proxy processing must likewise remain able to forward this reward even when local reward execution is disabled; gate only non-proxy events here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProcessRewards: false is intended to disable all rewards on that server, including rewards triggered by proxy-delivered votes. The guard here is therefore intentional and should not include an event.isBungee() exception.

Comment on lines +160 to +161

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward proxy-side delay rejections to the reward handler

When a network enables WaitUntilVoteDelay in bungeeconfig.yml with proxy-managed totals, the proxy rejects the vote at VotingPluginProxy.java:1719-1721 and returns before creating any backend PlayerVoteEvent. Therefore this new call is never reached, and WaitUntilVoteDelayRewards cannot run for votes rejected by the proxy even when the matching backend vote site configures it; the proxy rejection path needs to notify a backend or otherwise dispatch the configured reward before returning.

Useful? React with 👍 / 👎.

}
return;
}
plugin.getLogger()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,17 @@ public void status() {
}
}

private void sendVoteDelayRejected(String player, String uuid, String service, boolean playerOnline,
String playerServer) {
if (!playerOnline || playerServer == null || !getAllAvailableServers().contains(playerServer)) {
debug("Not sending vote delay rejection for " + player + " because the player is offline");
return;
Comment on lines +1504 to +1506

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Deliver delay-rejection rewards for offline players

When BungeeManageTotals rejects a vote while the player is offline, this guard discards the notification entirely, so even offline-capable rewards such as console commands can never run. The backend handler already accepts wasOnline == false, and the standalone listener dispatches the reward with an offline state, so route the rejection to an appropriate backend instead of returning solely because no current player server exists.

Useful? React with 👍 / 👎.

}

globalMessageProxyHandler.sendMessage(playerServer, 1,
VotingPluginWire.voteDelayRejected(player, uuid, service, true));
}

public String getWaitUntilDelaySiteFromService(String service) {
for (String site : getConfig().getWaitUntilVoteDelaySites()) {
if (getConfig().getWaitUntilVoteDelayService(site).equalsIgnoreCase(service)) {
Expand Down Expand Up @@ -1718,6 +1729,7 @@ private synchronized void vote(String player, String service, boolean realVote,

if (!checkVoteDelay(uuid, service, data)) {
log("Vote delay is not met for " + player + "/" + service + ", skipping vote");
sendVoteDelayRejected(player, uuid, service, playerOnline, playerServer);
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ private VotingPluginWire() {
public static final String SUB_VOTE = "Vote";
public static final String SUB_VOTE_ONLINE = "VoteOnline";
public static final String SUB_VOTE_UPDATE = "VoteUpdate";
public static final String SUB_VOTE_DELAY_REJECTED = "VoteDelayRejected";
public static final String SUB_VOTE_BROADCAST = "VoteBroadcast";
public static final String SUB_BUNGEE_TIME_CHANGE = "BungeeTimeChange";

Expand Down Expand Up @@ -108,6 +109,11 @@ public static JsonEnvelope voteOnline(String player, String uuid, String service
.put(K_NUM, num).put(K_NUMBER_OF_VOTES, numberOfVotes).build();
}

public static JsonEnvelope voteDelayRejected(String player, String uuid, String service, boolean wasOnline) {
return base(SUB_VOTE_DELAY_REJECTED).put(K_PLAYER, safe(player)).put(K_UUID, safe(uuid))
.put(K_SERVICE, safe(service)).put(K_WAS_ONLINE, wasOnline).build();
}

public static JsonEnvelope voteBroadcast(String uuid, String player, String service, long time, String totals) {
return base(SUB_VOTE_BROADCAST).put(K_UUID, safe(uuid)).put(K_PLAYER, safe(player))
.put(K_SERVICE, safe(service)).put(K_TIME, time).put(K_TOTALS, safe(totals)).build();
Expand Down Expand Up @@ -232,6 +238,26 @@ public static Vote readVote(JsonEnvelope env) {
broadcast, num, numberOfVotes);
}

public static final class VoteDelayRejected {
public final String player;
public final String uuid;
public final String service;
public final boolean wasOnline;

private VoteDelayRejected(String player, String uuid, String service, boolean wasOnline) {
this.player = player;
this.uuid = uuid;
this.service = service;
this.wasOnline = wasOnline;
}
}

public static VoteDelayRejected readVoteDelayRejected(JsonEnvelope env) {
Map<String, String> f = env.getFields();
return new VoteDelayRejected(safe(f.get(K_PLAYER)), safe(f.get(K_UUID)), safe(f.get(K_SERVICE)),
readBool(f, K_WAS_ONLINE, false));
}

public static final class VoteUpdate {
public final String uuid;
public final int votePartyCurrent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ public void giveRewards(VotingPluginUser user, boolean online, boolean bungee) {

}

/**
* Gives rewards configured for a vote rejected by WaitUntilVoteDelay.
*
* @param user the voting user
* @param online whether the player was online when the vote was received
* @param bungee whether the vote came through the proxy
*/
public void giveWaitUntilVoteDelayRewards(VotingPluginUser user, boolean online, boolean bungee) {
new RewardBuilder(plugin.getConfigVoteSites().getData(),
plugin.getConfigVoteSites().getWaitUntilVoteDelayRewardsPath(key)).setOnline(online)
.withPlaceHolder("ServiceSite", getServiceSite())
.withPlaceHolder("SiteName", getDisplayName())
.withPlaceHolder("VoteDelay", "" + getVoteDelay())
.withPlaceHolder("VoteURL", getVoteURL()).setServer(bungee).send(user);
}

public boolean hasRewards() {
return plugin.getRewardHandler().hasRewards(plugin.getConfigVoteSites().getData(),
plugin.getConfigVoteSites().getRewardsPath(key));
Expand Down
6 changes: 6 additions & 0 deletions VotingPlugin/src/main/resources/VoteSites.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ VoteSites:
# Recommend: false
WaitUntilVoteDelay: false

# Reward to run when a real vote is rejected because VoteDelay has not ended
# This supports standard rewards and the placeholders %ServiceSite%, %SiteName%, %VoteDelay%, and %VoteURL%
#WaitUntilVoteDelayRewards:
# Messages:
# Player: '&cYour vote was not accepted because the vote delay has not ended.'

# Reset vote delay each day (for certain sites that do this)
# Recommend: false
VoteDelayDaily: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.bencodez.simpleapi.servercomm.codec.JsonEnvelope;
import com.bencodez.votingplugin.proxy.VotingPluginWire;
import com.bencodez.votingplugin.proxy.VotingPluginWire.Vote;
import com.bencodez.votingplugin.proxy.VotingPluginWire.VoteDelayRejected;

/**
* Tests proxy vote wire encoding and decoding.
Expand Down Expand Up @@ -39,4 +40,18 @@ public void voteOnlineRoundTripAllowsMissingVoteId() {

assertNull(vote.voteId);
}

@Test
public void voteDelayRejectedRoundTripPreservesContext() {
String uuid = UUID.randomUUID().toString();
JsonEnvelope envelope = VotingPluginWire.voteDelayRejected("Player", uuid, "Service", true);

VoteDelayRejected rejected = VotingPluginWire.readVoteDelayRejected(envelope);

assertEquals(VotingPluginWire.SUB_VOTE_DELAY_REJECTED, envelope.getSubChannel());
assertEquals("Player", rejected.player);
assertEquals(uuid, rejected.uuid);
assertEquals("Service", rejected.service);
assertEquals(true, rejected.wasOnline);
}
}
Loading