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 @@ -566,6 +566,7 @@ private void startServices() {
try { net.tfminecraft.simplefactions.identity.death.CharacterDeaths.reconcile(); }
catch (Exception error) { net.tfminecraft.simplefactions.economy.EconomyRuntime.failClosed(error); return; }
net.tfminecraft.simplefactions.guild.LeaderlessGuilds.closeExpired(System.currentTimeMillis());
net.tfminecraft.simplefactions.guild.HeldRealms.closeExpired(System.currentTimeMillis(), getDataFolder().toPath());
}, 72000L, 72000L);
net.tfminecraft.simplefactions.identity.LeaderCharacterListener.refresh(null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,10 +425,11 @@ private boolean saveFaction(Faction f, boolean durable) {
gd.leader = g.getLeader();
gd.leaderCharacter = g.getLeaderCharacter();
gd.leaderCharacterOf = g.getLeaderCharacterOf();
if (g.isLeaderless()) {
if (g.isLeaderless() || g.isHeld()) {
gd.deadLeader = g.getDeadLeader();
gd.leaderlessSince = g.getLeaderlessSince();
gd.emptySince = g.getEmptySince();
if (g.getClosingSince() > 0) gd.closingSince = g.getClosingSince();
gd.leaderOffer = g.getLeaderOffer();
}
gd.rgb = g.getRGB();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public class GuildData {
/** When a leaderless guild was last left with no members; it closes 30 days later. */
@SerializedName("empty since")
public Long emptySince;
/** When a held realm's closure began. */
@SerializedName("closing since")
public Long closingSince;
/** The character the realm leader has offered the empty seat to. */
@SerializedName("leader offer")
public String leaderOffer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ private static FactionUpkeepPlan afterProvinceLoss(FactionUpkeepPlan plan, List<

private static Integer removal(Faction faction) {
List<Integer> provinces = faction.getProvinces();
if (CivilWarBorderLock.isLocked(faction) || !TitleManager.overProvinceCap(faction) || provinces.isEmpty()) return null;
if (CivilWarBorderLock.isLocked(faction) || net.tfminecraft.simplefactions.guild.HeldRealms.isHeld(faction)
|| !TitleManager.overProvinceCap(faction) || provinces.isEmpty()) return null;
int remove = provinces.getLast();
if (remove == faction.getCapital() && provinces.size() > 1) remove = provinces.get(provinces.size() - 2);
return remove == faction.getCapital() ? null : remove;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public record FactionUpkeepPlan(String faction, Map<String, Integer> regimentSlo
long charge, double armyLedgerCost, double installationLedgerCost) {

public static FactionUpkeepPlan calculate(Faction faction) {
if (net.tfminecraft.simplefactions.guild.HeldRealms.isHeld(faction)) return paused(faction);
List<Regiment> regiments = new ArrayList<>();
if (faction.getMilitary() != null) {
for (Regiment regiment : faction.getMilitary().getRegiments()) {
Expand Down Expand Up @@ -73,6 +74,20 @@ public static FactionUpkeepPlan calculate(Faction faction) {
return new FactionUpkeepPlan(faction.getId(), slots, installations, construction, charge, army, ledgerCost);
}

/** A held realm pays nothing and keeps its army and installations as they are. */
private static FactionUpkeepPlan paused(Faction faction) {
Map<String, Integer> slots = new LinkedHashMap<>();
if (faction.getMilitary() != null) {
for (Regiment regiment : faction.getMilitary().getRegiments()) {
if (regiment != null) slots.put(regiment.getId(), regiment.getCurrentSlots());
}
}
List<InstallationData> installations = faction.getInstallationHandler().getAll().stream()
.sorted(Comparator.comparing(Installation::getId)).map(Installation::toData).toList();
return new FactionUpkeepPlan(faction.getId(), slots, new ArrayList<>(installations),
faction.getInstallationHandler().serializeConstruction(), 0, 0, 0);
}

private static double armyCost(List<Regiment> regiments, double factor) {
double raw = 0;
for (Regiment regiment : regiments) raw += regiment.getTotalUpkeep();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ private void liquidate(Guild guild, boolean moved) {
change.incarnation = guild.getIncarnationId();
guild.getBranches().forEach((key, branch) -> change.branches.put(key, branch.getLevel()));
changes.guilds.add(change);
if (!moved || guild.getBank() == null || net.tfminecraft.simplefactions.inactivity.InactivityService.isFullyInactive(guild.getMembers())) return;
if (!moved || guild.getBank() == null || net.tfminecraft.simplefactions.inactivity.InactivityService.isFullyInactive(guild.getMembers())
|| net.tfminecraft.simplefactions.guild.HeldRealms.isHeld(guild.getFaction())) return;
int size = change.branches.values().stream().mapToInt(Integer::intValue).sum();
Random random = new Random(day ^ guild.getIncarnationId().getMostSignificantBits() ^ guild.getIncarnationId().getLeastSignificantBits());
for (int guard = 0; balances.get(account(guild)) < 0 && size > 0 && guard < 1000; guard++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import net.tfminecraft.simplefactions.api.SimpleFactionsApi;
import net.tfminecraft.simplefactions.economy.settlement.SettlementGuilds;
import net.tfminecraft.simplefactions.guild.Guild;
import net.tfminecraft.simplefactions.guild.HeldRealms;
import net.tfminecraft.simplefactions.guild.LeaderlessGuilds;
import net.tfminecraft.simplefactions.identity.Members;
import net.tfminecraft.simplefactions.identity.death.CharacterDeaths;
Expand All @@ -30,7 +31,7 @@ public DeathGuild deathGuild(UUID characterId, UUID deathId) {
case UNAVAILABLE -> DeathGuild.UNAVAILABLE;
case NONE -> DeathGuild.NONE;
case GUILD -> {
if (LeaderlessGuilds.closing(recorded.incarnation())) yield DeathGuild.UNAVAILABLE;
if (closing(recorded.incarnation())) yield DeathGuild.UNAVAILABLE;
Guild live = byIncarnation(recorded.incarnation());
yield DeathGuild.guild(recorded.incarnation(), live != null,
live == null ? Optional.empty() : leader(live), Optional.ofNullable(recorded.realm()));
Expand All @@ -40,7 +41,7 @@ public DeathGuild deathGuild(UUID characterId, UUID deathId) {

@Override
public GuildLookup guild(UUID incarnation) {
if (incarnation == null || !SimpleFactionsApi.ready() || LeaderlessGuilds.closing(incarnation)) {
if (incarnation == null || !SimpleFactionsApi.ready() || closing(incarnation)) {
return GuildLookup.UNAVAILABLE;
}
Guild guild = byIncarnation(incarnation);
Expand All @@ -57,7 +58,12 @@ public GuildLookup guildByName(String name) {
.filter(guild -> name.trim().equalsIgnoreCase(guild.getName()))
.toList();
if (matches.size() > 1) return GuildLookup.UNAVAILABLE;
return matches.isEmpty() ? GuildLookup.GONE : GuildLookup.live(ref(matches.getFirst()));
return matches.isEmpty() ? GuildLookup.GONE : guild(matches.getFirst().getIncarnationId());
}

/** A guild being closed, or a held realm, is neither live nor gone yet. */
private static boolean closing(UUID incarnation) {
return LeaderlessGuilds.closing(incarnation) || HeldRealms.unsettled(incarnation);
}

private static Guild byIncarnation(UUID incarnation) {
Expand Down
22 changes: 21 additions & 1 deletion src/main/java/net/tfminecraft/simplefactions/guild/Guild.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public class Guild {
private long leaderlessSince;
private long emptySince;
private String leaderOffer;
private long closingSince;

public Guild(Faction f) {
host = f;
Expand Down Expand Up @@ -194,6 +195,7 @@ public Guild(GuildData data, Faction host) {
this.leaderlessSince = data.leaderlessSince == null ? 0 : data.leaderlessSince;
this.emptySince = data.emptySince == null ? 0 : data.emptySince;
this.leaderOffer = data.leaderOffer;
this.closingSince = data.closingSince == null ? 0 : data.closingSince;
this.rgb = data.rgb;
this.capital = data.capital;
this.stance = data.stance != null ? Stance.valueOf(data.stance) : Stance.NEUTRAL;
Expand Down Expand Up @@ -483,6 +485,10 @@ public void addMember(String p) {
}
consumeInvite(p);
members.add(p);
// Anyone let into a held realm stops its week; the hourly check starts it again once empty.
Guild main = isBase() ? this
: host == null || host.getGuildHandler() == null ? null : host.getGuildHandler().getGuild(host.getId());
if (main != null && main.isHeld()) main.emptySince = 0;
}
public void kick(String member) {
String stored = find(members, member);
Expand Down Expand Up @@ -551,7 +557,12 @@ public void restoreLeaderless(String deadKey, long since, long empty, String off

/** Starts or stops the empty clock of a leaderless guild; true when it changed. */
public boolean trackEmpty(long now) {
long next = members.isEmpty() ? (emptySince > 0 ? emptySince : now) : 0;
return trackEmpty(now, members.isEmpty());
}

/** As {@link #trackEmpty(long)}, with emptiness judged by the caller (a held realm counts every guild). */
public boolean trackEmpty(long now, boolean empty) {
long next = empty ? (emptySince > 0 ? emptySince : now) : 0;
if (next == emptySince) return false;
emptySince = next;
return true;
Expand All @@ -562,10 +573,18 @@ public boolean isLeaderless() {
return !isBase() && leaderlessSince > 0 && (leader == null || leader.isBlank());
}

/** A realm's own guild whose last member died: the realm is held for a revive (HeldRealms). */
public boolean isHeld() {
return isBase() && leaderlessSince > 0 && (getLeader() == null || getLeader().isBlank());
}

public String getDeadLeader() { return deadLeader; }
public long getLeaderlessSince() { return leaderlessSince; }
public long getEmptySince() { return emptySince; }
public String getLeaderOffer() { return leaderOffer; }
/** When a held realm's closure began; from then on it closes even if its leader is revived. */
public long getClosingSince() { return closingSince; }
public void markClosing(long at) { closingSince = at; }

public void offerLeadership(String key) { leaderOffer = key; }

Expand All @@ -576,6 +595,7 @@ private void clearLeaderless() {
leaderlessSince = 0;
emptySince = 0;
leaderOffer = null;
closingSince = 0;
}
public boolean isLeader(String p) {
String currentLeader = getLeader();
Expand Down
Loading
Loading