Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ddfebbc
feat: mayors
ArikSquad Feb 24, 2026
5dfb718
Merge branch 'feat/new-hub' into feat/mayors
ArikSquad Feb 24, 2026
26ba41a
feat: election service
ArikSquad Feb 25, 2026
b4840a3
Merge remote-tracking branch 'origin/master' into feat/mayors
ArikSquad Feb 26, 2026
640e877
feat: teleporter warps
ArikSquad Feb 26, 2026
3e816eb
feat: improve fall damage
ArikSquad Feb 26, 2026
112dbba
feat: candidates and voting properly
ArikSquad Feb 26, 2026
3cf5c52
feat: scalable and now finished
ArikSquad Feb 26, 2026
7453387
feat: calendar now autoupdates
ArikSquad Feb 26, 2026
ed87bc7
fix: move Swofty out of the wall
ArikSquad Feb 26, 2026
facc29b
merge: resolve conflicts merging master into feat/mayors
Swofty-Developments Feb 26, 2026
489e32e
feat(i18n): add locale-aware infrastructure for per-player translations
Swofty-Developments Feb 27, 2026
28190f5
feat(i18n): migrate all callsites to use per-player locale
Swofty-Developments Feb 27, 2026
7c3ab1c
feat(elections): scalable vote storage with atomic MongoDB ops and mu…
Swofty-Developments Feb 27, 2026
a1d126c
refactor(elections): remove mutable enum state and delegate operation…
Swofty-Developments Feb 27, 2026
7de8cda
refactor(elections): deduplicate NPCs with abstract bases, fix Electi…
Swofty-Developments Feb 27, 2026
032575c
feat(i18n): migrate election GUIs, NPCs, and holograms to translation…
Swofty-Developments Feb 27, 2026
0b9d44a
chore(i18n): convert election translation strings from legacy § codes…
Swofty-Developments Feb 27, 2026
8daa33b
Merge remote-tracking branch 'origin/master' into feat/mayors
ArikSquad Mar 23, 2026
1cf4f15
fix: correct class names
ArikSquad Apr 6, 2026
5353092
fix: make code make more sense
ArikSquad Apr 6, 2026
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
2 changes: 2 additions & 0 deletions commons/src/main/java/net/swofty/commons/ServiceType.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public enum ServiceType {
ORCHESTRATOR,
FRIEND,
PUNISHMENT,
ELECTION,
;
}
55 changes: 55 additions & 0 deletions commons/src/main/java/net/swofty/commons/StringUtility.java
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,18 @@ public static String getAsRomanNumeral(int num) {
return roman.toString();
}

/**
* Capitalizes the first letter of the input string and lowercases the rest.
* @param input The string to capitalize.
* @return The input string with the first letter capitalized and the rest lowercased. If the input is null or empty, it returns the input as is.
*/
public static String capitalize(String input) {
if (input == null || input.isEmpty()) {
return input;
}
return input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase();
}

public static String getTextFromComponent(Component component) {
if (component == null)
throw new IllegalArgumentException("Component cannot be null");
Expand Down Expand Up @@ -342,6 +354,49 @@ public static List<String> splitByWordAndLength(String string, int splitLength)
return result;
}

public static List<String> splitByWordAndLengthKeepLegacyColor(String string, int splitLength) {
List<String> result = new ArrayList<>();
String lastColorCode = "";

for (String line : string.split("\n", -1)) {
if (line.isEmpty()) {
result.add("");
continue;
}

StringBuilder currentLine = new StringBuilder(lastColorCode);

for (String word : line.split(" ")) {
if (word.isEmpty()) continue;

int extraSpace = currentLine.length() == lastColorCode.length() ? 0 : 1;

if (currentLine.length() + extraSpace + word.length() > splitLength) {
if (currentLine.length() > lastColorCode.length()) {
result.add(currentLine.toString());
currentLine = new StringBuilder(lastColorCode);
}
} else if (extraSpace == 1) {
currentLine.append(' ');
}

currentLine.append(word);

// this wont work with bold/italic and those but if you need those don't use this method
int colorIndex = word.lastIndexOf('§');
if (colorIndex != -1 && colorIndex < word.length() - 1) {
lastColorCode = "§" + word.charAt(colorIndex + 1);
}
}

if (currentLine.length() > lastColorCode.length()) {
result.add(currentLine.toString());
}
}

return result;
}

public static List<String> splitByNewLine(String string) {
return new ArrayList<>(Arrays.asList(string.split("\n", -1)));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package net.swofty.commons.protocol.objects.election;

import net.swofty.commons.protocol.ProtocolObject;
import net.swofty.commons.protocol.Serializer;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class CastVoteProtocolObject
extends ProtocolObject<CastVoteProtocolObject.CastVoteMessage,
CastVoteProtocolObject.CastVoteResponse> {

@Override
public Serializer<CastVoteMessage> getSerializer() {
return new Serializer<>() {
@Override
public String serialize(CastVoteMessage value) {
JSONObject json = new JSONObject();
json.put("accountId", value.accountId().toString());
json.put("candidateName", value.candidateName());
return json.toString();
}

@Override
public CastVoteMessage deserialize(String json) {
JSONObject obj = new JSONObject(json);
return new CastVoteMessage(
UUID.fromString(obj.getString("accountId")),
obj.getString("candidateName")
);
}

@Override
public CastVoteMessage clone(CastVoteMessage value) {
return value;
}
};
}

@Override
public Serializer<CastVoteResponse> getReturnSerializer() {
return new Serializer<>() {
@Override
public String serialize(CastVoteResponse value) {
JSONObject json = new JSONObject();
json.put("success", value.success());
json.put("tallies", value.tallies() == null ? null : new JSONObject(value.tallies()));
return json.toString();
}

@Override
public CastVoteResponse deserialize(String json) {
JSONObject obj = new JSONObject(json);
Map<String, Long> tallies = null;
if (!obj.isNull("tallies")) {
tallies = new HashMap<>();
JSONObject talliesObject = obj.getJSONObject("tallies");
for (String key : talliesObject.keySet()) {
tallies.put(key, talliesObject.getLong(key));
}
}
return new CastVoteResponse(
obj.getBoolean("success"),
tallies
);
}

@Override
public CastVoteResponse clone(CastVoteResponse value) {
return value;
}
};
}

public record CastVoteMessage(UUID accountId, String candidateName) {}

public record CastVoteResponse(boolean success, Map<String, Long> tallies) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package net.swofty.commons.protocol.objects.election;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import net.swofty.commons.protocol.ProtocolObject;
import net.swofty.commons.protocol.Serializer;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.List;

public class GetCandidatesProtocolObject
extends ProtocolObject<GetCandidatesProtocolObject.GetCandidatesMessage,
GetCandidatesProtocolObject.GetCandidatesResponse> {

@Override
public Serializer<GetCandidatesMessage> getSerializer() {
return new Serializer<>() {
@Override
public String serialize(GetCandidatesMessage value) {
return "";
}

@Override
public GetCandidatesMessage deserialize(String json) {
return new GetCandidatesMessage();
}

@Override
public GetCandidatesMessage clone(GetCandidatesMessage value) {
return value;
}
};
}

@Override
public Serializer<GetCandidatesResponse> getReturnSerializer() {
return new Serializer<>() {
private final Gson gson = new Gson();

@Override
public String serialize(GetCandidatesResponse value) {
JSONObject json = new JSONObject();
json.put("electionOpen", value.electionOpen());
json.put("candidates", value.candidates() == null ? null : new JSONArray(gson.toJson(value.candidates())));
return json.toString();
}

@Override
public GetCandidatesResponse deserialize(String json) {
JSONObject obj = new JSONObject(json);
boolean open = obj.getBoolean("electionOpen");
List<CandidateInfo> candidates = List.of();
if (!obj.isNull("candidates")) {
candidates = gson.fromJson(
obj.getJSONArray("candidates").toString(),
new TypeToken<List<CandidateInfo>>() {}.getType()
);
}
return new GetCandidatesResponse(open, candidates);
}

@Override
public GetCandidatesResponse clone(GetCandidatesResponse value) {
return value;
}
};
}

public record GetCandidatesMessage() {}

public record GetCandidatesResponse(
boolean electionOpen,
List<CandidateInfo> candidates
) {}

public record CandidateInfo(
String mayorName,
List<String> activePerks,
long votes,
double votePercentage
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package net.swofty.commons.protocol.objects.election;

import net.swofty.commons.protocol.ProtocolObject;
import net.swofty.commons.protocol.Serializer;
import org.json.JSONObject;

public class GetElectionDataProtocolObject
extends ProtocolObject<GetElectionDataProtocolObject.GetElectionDataMessage,
GetElectionDataProtocolObject.GetElectionDataResponse> {

@Override
public Serializer<GetElectionDataMessage> getSerializer() {
return new Serializer<>() {
@Override
public String serialize(GetElectionDataMessage value) {
return "";
}

@Override
public GetElectionDataMessage deserialize(String json) {
return new GetElectionDataMessage();
}

@Override
public GetElectionDataMessage clone(GetElectionDataMessage value) {
return value;
}
};
}

@Override
public Serializer<GetElectionDataResponse> getReturnSerializer() {
return new Serializer<>() {
@Override
public String serialize(GetElectionDataResponse value) {
JSONObject json = new JSONObject();
json.put("found", value.found());
json.put("data", value.serializedData());
return json.toString();
}

@Override
public GetElectionDataResponse deserialize(String json) {
JSONObject obj = new JSONObject(json);
return new GetElectionDataResponse(
obj.getBoolean("found"),
obj.optString("data", null)
);
}

@Override
public GetElectionDataResponse clone(GetElectionDataResponse value) {
return value;
}
};
}

public record GetElectionDataMessage() {}

public record GetElectionDataResponse(
boolean found,
String serializedData
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package net.swofty.commons.protocol.objects.election;

import net.swofty.commons.protocol.ProtocolObject;
import net.swofty.commons.protocol.Serializer;
import org.json.JSONObject;

import java.util.UUID;

public class GetPlayerVoteProtocolObject
extends ProtocolObject<GetPlayerVoteProtocolObject.GetPlayerVoteMessage,
GetPlayerVoteProtocolObject.GetPlayerVoteResponse> {

@Override
public Serializer<GetPlayerVoteMessage> getSerializer() {
return new Serializer<>() {
@Override
public String serialize(GetPlayerVoteMessage value) {
JSONObject json = new JSONObject();
json.put("accountId", value.accountId().toString());
return json.toString();
}

@Override
public GetPlayerVoteMessage deserialize(String json) {
JSONObject obj = new JSONObject(json);
return new GetPlayerVoteMessage(UUID.fromString(obj.getString("accountId")));
}

@Override
public GetPlayerVoteMessage clone(GetPlayerVoteMessage value) {
return value;
}
};
}

@Override
public Serializer<GetPlayerVoteResponse> getReturnSerializer() {
return new Serializer<>() {
@Override
public String serialize(GetPlayerVoteResponse value) {
JSONObject json = new JSONObject();
json.put("candidateName", value.candidateName());
return json.toString();
}

@Override
public GetPlayerVoteResponse deserialize(String json) {
JSONObject obj = new JSONObject(json);
return new GetPlayerVoteResponse(obj.optString("candidateName", null));
}

@Override
public GetPlayerVoteResponse clone(GetPlayerVoteResponse value) {
return value;
}
};
}

public record GetPlayerVoteMessage(UUID accountId) {}

public record GetPlayerVoteResponse(String candidateName) {}
}
Loading
Loading