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
15 changes: 12 additions & 3 deletions src/main/java/net/tfminecraft/dowsing/managers/NodeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,20 @@ public static void released(Guild g, Node n) {
passCounts.computeIfPresent(g.getId(), (id, count) -> count - 1);
}

/** Room for new nodes, from the guild's active members. */
public static Integer getNodeCapacity(Guild g) {
return capacityFor(g, net.tfminecraft.dowsing.utils.ActiveMembers.count(g));
}

/** Room to keep the nodes a guild already holds, from all its members, so going inactive does not cost a node. */
public static Integer getHeldNodeCapacity(Guild g) {
return capacityFor(g, g == null || g.getMembers() == null ? 0 : g.getMembers().size());
}

private static Integer capacityFor(Guild g, int members) {
int capacity = 1;
if(g == null) return capacity;
if(Cache.extraCapacity) {
int members = g.getMembers().size();
int added = (int) Math.floorDiv(members, Cache.membersPerCapacity);
if(added > Cache.maxMemberCapacity) {
added = Cache.maxMemberCapacity;
Expand Down Expand Up @@ -450,8 +459,8 @@ private void tryCreateNode(Player p, Location loc, NodeBlock b, Cancellable e) {
e.setCancelled(true);
return;
}
if(g.getMembers().size() < Cache.minMembersForNode && !b.isSpecial()){
p.sendMessage("§cYou need at least "+Cache.minMembersForNode+" members in your guild to have a node!");
if(net.tfminecraft.dowsing.utils.ActiveMembers.count(g) < Cache.minMembersForNode && !b.isSpecial()){
p.sendMessage("§cYou need at least "+Cache.minMembersForNode+" active members in your guild to have a node!");
e.setCancelled(true);
return;
}
Expand Down
11 changes: 6 additions & 5 deletions src/main/java/net/tfminecraft/dowsing/objects/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,9 @@ public boolean canHold(Guild g){
if(held(g)) return true;
Player p = Members.player(g.getLeader());
if(p != null && p.isOnline() && Permissions.isAdmin(p)) return true;
// Holding counts every member, active or not; only new claims need active ones.
if(g.getMembers().size() < Cache.minMembersForNode) return false;
if(NodeManager.getNodeAmount(g)-NodeManager.getNodeCapacity(g) > 0) return false;
if(NodeManager.getNodeAmount(g)-NodeManager.getHeldNodeCapacity(g) > 0) return false;
return true;
}

Expand All @@ -328,8 +329,8 @@ public static boolean held(Guild g) {
public boolean canClaim(Player p, Guild g){
if(Permissions.isAdmin(p)) return true;
if(!isClaimable()) return false;
if(g.getMembers().size() < Cache.minMembersForNode){
p.sendMessage("§cYou need at least "+Cache.minMembersForNode+" members in your guild to claim this node!");
if(net.tfminecraft.dowsing.utils.ActiveMembers.count(g) < Cache.minMembersForNode){
p.sendMessage("§cYou need at least "+Cache.minMembersForNode+" active members in your guild to claim this node!");
return false;
}
if(NodeManager.getNodeAmount(g)-NodeManager.getNodeCapacity(g) >= 0 && !block.isSpecial()) {
Expand Down Expand Up @@ -584,14 +585,14 @@ public void deActivate() {
public void growEfficiency() {
// A held realm has no members, which would cap its nodes' efficiency at 0 until it is taken on.
if(resolveGuild() == null || held(guild)) return;
int members = guild.getMembers().size();
int members = net.tfminecraft.dowsing.utils.ActiveMembers.count(guild);
double growth = Math.min(1.0, members*Cache.efficiencyGrowthPerMember);
updateEfficiency(growth);
}

public double getMaxEfficiency() {
if(resolveGuild() == null) return 0;
return Math.min(100.0, guild.getMembers().size()*Cache.maxEfficiencyPerMember);
return Math.min(100.0, net.tfminecraft.dowsing.utils.ActiveMembers.count(guild)*Cache.maxEfficiencyPerMember);
}

public void updateEfficiency(double eff) {
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/net/tfminecraft/dowsing/utils/ActiveMembers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.tfminecraft.dowsing.utils;

import net.tfminecraft.simplefactions.guild.Guild;
import net.tfminecraft.simplefactions.inactivity.InactivityService;

/**
* The guild members who count toward nodes. SimpleFactions marks a member inactive after
* weeks offline; they stay in the guild but add no node capacity, do not meet the minimum
* member count and do not raise node efficiency until they log in again.
*/
public final class ActiveMembers {
private ActiveMembers() {}

public static int count(Guild guild) {
if (guild == null || guild.getMembers() == null) return 0;
int active = 0;
for (String member : guild.getMembers()) {
if (!inactive(member)) active++;
}
return active;
}

/** A member whose inactivity cannot be read counts, as before inactivity was checked. */
static boolean inactive(String member) {
try {
return InactivityService.isMemberInactive(member);
} catch (LinkageError | RuntimeException unreadable) {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ void placementRequiresGuildMembershipMinimumMembersAndAvailableCapacity() {
verify(f.player).sendMessage(contains("need to have a guild"));
f.factions.when(() -> FactionManager.getGuildByMember(f.player)).thenReturn(f.guild);
Cache.minMembersForNode = 3; event = f.place(); f.manager.placeVanillaNode(event);
verify(event).setCancelled(true); verify(f.player).sendMessage(contains("at least 3 members"));
verify(event).setCancelled(true); verify(f.player).sendMessage(contains("at least 3 active members"));
Cache.minMembersForNode = 1; NodeManager.nodes.add(f.nodeAt(f.remoteLocation()));
event = f.place(); f.manager.placeVanillaNode(event); verify(event).setCancelled(true);
verify(f.player).sendMessage(contains("filled its node capacity"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ void claimAndRetentionEnforceMembershipAndCapacityWithAdminAndSpecialExceptions(
Cache.minMembersForNode = 3;
assertFalse(node.canHold(null)); assertFalse(node.canHold(f.guild));
assertFalse(node.canClaim(player, f.guild));
verify(player).sendMessage(contains("at least 3 members"));
verify(player).sendMessage(contains("at least 3 active members"));
Cache.minMembersForNode = 1;
assertTrue(node.canHold(f.guild)); assertTrue(node.canClaim(player, f.guild));
Node owned = f.owned(); NodeManager.nodes.add(owned);
Expand Down
80 changes: 80 additions & 0 deletions src/test/java/net/tfminecraft/dowsing/utils/ActiveMembersTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package net.tfminecraft.dowsing.utils;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import net.tfminecraft.dowsing.Cache;
import net.tfminecraft.dowsing.managers.NodeManager;
import net.tfminecraft.simplefactions.guild.Guild;
import net.tfminecraft.simplefactions.inactivity.InactivityService;

class ActiveMembersTest {
private MockedStatic<InactivityService> inactivity;
private Boolean previousExtra;
private Integer previousPer;
private int previousMax;
private Map<String, Integer> previousBought;

@BeforeEach void setUp() {
inactivity = mockStatic(InactivityService.class);
inactivity.when(() -> InactivityService.isMemberInactive(anyString()))
.thenAnswer(call -> call.getArgument(0, String.class).startsWith("away"));
previousExtra = Cache.extraCapacity; previousPer = Cache.membersPerCapacity; previousMax = Cache.maxMemberCapacity;
previousBought = new HashMap<>(NodeManager.extraCapacityByGuild); NodeManager.extraCapacityByGuild.clear();
}

@AfterEach void tearDown() {
inactivity.close();
Cache.extraCapacity = previousExtra; Cache.membersPerCapacity = previousPer; Cache.maxMemberCapacity = previousMax;
NodeManager.extraCapacityByGuild.clear(); NodeManager.extraCapacityByGuild.putAll(previousBought);
}

private static Guild guild(String... members) {
Guild guild = mock(Guild.class);
when(guild.getId()).thenReturn("guild");
when(guild.getMembers()).thenReturn(new ArrayList<>(List.of(members)));
return guild;
}

@Test
void inactiveMembersAreLeftOutOfTheCount() {
assertEquals(2, ActiveMembers.count(guild("ann", "away1", "bob", "away2")));
assertEquals(0, ActiveMembers.count(guild("away1")));
assertEquals(0, ActiveMembers.count(null));
Guild noRoster = mock(Guild.class);
assertEquals(0, ActiveMembers.count(noRoster));
}

@Test
void aMemberWhoseInactivityCannotBeReadStillCounts() {
inactivity.when(() -> InactivityService.isMemberInactive("broken")).thenThrow(new IllegalStateException("unreadable"));
inactivity.when(() -> InactivityService.isMemberInactive("old")).thenThrow(new NoSuchMethodError("older SimpleFactions"));
assertEquals(3, ActiveMembers.count(guild("broken", "old", "ann", "away1")));
}

@Test
void nodeCapacityGrowsOnlyWithActiveMembers() {
Cache.extraCapacity = true; Cache.membersPerCapacity = 2; Cache.maxMemberCapacity = 10;
assertEquals(2, NodeManager.getNodeCapacity(guild("ann", "bob", "away1", "away2", "away3")));
assertEquals(3, NodeManager.getNodeCapacity(guild("ann", "bob", "cid", "dan", "away1")));
}

@Test
void nodesAlreadyHeldAreKeptOnEveryMember() {
Cache.extraCapacity = true; Cache.membersPerCapacity = 2; Cache.maxMemberCapacity = 10;
assertEquals(3, NodeManager.getHeldNodeCapacity(guild("ann", "bob", "away1", "away2", "away3")));
assertEquals(1, NodeManager.getHeldNodeCapacity(null));
assertEquals(1, NodeManager.getHeldNodeCapacity(mock(Guild.class)));
}
}