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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<rpcharacters.version>2.0.1</rpcharacters.version>
<tlibs.version>2.0.0</tlibs.version>
<vehicleframework.version>2.0.1</vehicleframework.version>
<vfbuilders.version>2.0.4</vfbuilders.version>
<vfbuilders.version>2.1.0</vfbuilders.version>
</properties>

<dependencies>
Expand Down
87 changes: 87 additions & 0 deletions src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@
import net.tfminecraft.simplefactions.vehicles.registry.VehicleRegistryClaimListener;
import net.tfminecraft.simplefactions.vehicles.registry.VehicleRegistryClaimService;
import net.tfminecraft.simplefactions.vehicles.registry.VehicleRegistryPersistence;
import net.tfminecraft.simplefactions.vehicles.fees.VehicleFeeConfirmations;
import net.tfminecraft.simplefactions.vehicles.fees.VehicleFeeStore;
import net.tfminecraft.simplefactions.vehicles.fees.VehicleReclaimFeeListener;
import net.tfminecraft.simplefactions.vehicles.handover.VehicleHandoverListener;
import net.tfminecraft.simplefactions.vehicles.handover.VehicleHandoverService;
import net.tfminecraft.simplefactions.vehicles.handover.VehicleHandoverSessionManager;
import net.tfminecraft.simplefactions.vehicles.maintenance.VehicleMaintenanceDecayTask;
import net.tfminecraft.simplefactions.vehicles.maintenance.VehicleMaintenancePayListener;
import net.tfminecraft.simplefactions.vehicles.maintenance.VehicleMaintenancePayService;
Expand Down Expand Up @@ -219,6 +225,16 @@ public class SimpleFactions extends JavaPlugin{
new VehicleMaintenancePayListener(
vehicleMaintenancePaySessionManager,
vehicleMaintenancePayService);
private final VehicleFeeStore vehicleFeeStore = new VehicleFeeStore();
private final VehicleFeeConfirmations vehicleFeeConfirmations = new VehicleFeeConfirmations();
private final VehicleHandoverSessionManager vehicleHandoverSessionManager = new VehicleHandoverSessionManager();
private final VehicleHandoverService vehicleHandoverService =
new VehicleHandoverService(vehicleRegistry, vehicleFeeStore, this::saveVehicleFees);
private final VehicleHandoverListener vehicleHandoverListener = new VehicleHandoverListener(
vehicleHandoverSessionManager, vehicleHandoverService, vehicleFeeConfirmations);
private final VehicleReclaimFeeListener vehicleReclaimFeeListener =
new VehicleReclaimFeeListener(vehicleFeeStore, vehicleFeeConfirmations, this::saveVehicleFees);
private boolean registrationFeeRegistered;
private boolean vehicleIntegrationRegistered = false;
private boolean constructionFreezeRegistered;
private final PlayerEconomyManager playerEconomyManager = new PlayerEconomyManager();
Expand Down Expand Up @@ -249,6 +265,8 @@ public void onEnable() {
new File(getDataFolder(), "Cache"),
vehicleMaintenanceStore);
vehicleMaintenancePersistence.load();
vehicleFeeStore.bind(new File(getDataFolder(), "Cache"));
vehicleFeeStore.load();
registerVehicleIntegrationHooks();
if (Cache.mapEnabled && !getServer().getPluginManager().isPluginEnabled("TFMCWeb")) {
getLogger().severe(
Expand Down Expand Up @@ -528,6 +546,50 @@ public FactionVehicleGiveService getFactionVehicleGiveService() {
return factionVehicleGiveService;
}

public VehicleHandoverSessionManager getVehicleHandoverSessionManager() {
return vehicleHandoverSessionManager;
}

public VehicleHandoverService getVehicleHandoverService() {
return vehicleHandoverService;
}

public void saveVehicleFees() {
vehicleFeeStore.save();
}

/** Remembers who a vehicle was just handed to, for the transfer fee on a later release. */
public void recordVehicleOwner(String vehicleUuid, String playerName) {
vehicleFeeStore.setLastOwner(vehicleUuid, playerName);
vehicleFeeStore.save();
}

/**
* Records the current owner of every personal vehicle, so a vehicle released before
* anyone built or claimed it through SimpleFactions still has a last owner.
*/
public void recordVehicleOwners() {
if (!getServer().getPluginManager().isPluginEnabled("VehicleFramework")) {
return;
}
try {
for (net.tfminecraft.vehicleframework.data.OwnedVehicleSummary vehicle
: net.tfminecraft.simplefactions.vehicles.registry.VehicleOwnershipQueries.allPersonalVehicles(vehicleRegistry)) {
String owner = net.tfminecraft.simplefactions.vehicles.registry.VehicleOwnershipQueries
.playerNameFromOwner(vehicle.getOwner());
if (owner != null) {
vehicleFeeStore.setLastOwner(vehicle.getUuid(), owner);
}
}
for (net.tfminecraft.simplefactions.vehicles.registry.PlayerVehicleRecord record : vehicleRegistry.getAll()) {
vehicleFeeStore.forgetVehicle(record.getVehicleUuid());
}
vehicleFeeStore.save();
} catch (RuntimeException | LinkageError e) {
getLogger().warning("Could not record vehicle owners for transfer fees: " + e);
}
}

public InstallationVehicleUnberthService getInstallationVehicleUnberthService() {
return installationVehicleUnberthService;
}
Expand All @@ -549,6 +611,7 @@ public boolean saveVehicleRegistry() {
if (vehicleMaintenancePersistence != null) {
vehicleMaintenancePersistence.save();
}
vehicleFeeStore.save();
return saved;
}

Expand Down Expand Up @@ -584,11 +647,31 @@ public void onPluginEnable(PluginEnableEvent event) {
|| "VehicleFramework".equalsIgnoreCase(event.getPlugin().getName())) {
registerVehicleIntegration();
registerConstructionFreeze();
registerRegistrationFee();
}
}
}, this);
registerVehicleIntegration();
registerConstructionFreeze();
registerRegistrationFee();
}

private void registerRegistrationFee() {
if (registrationFeeRegistered || !getServer().getPluginManager().isPluginEnabled("VFBuilders")
|| !getServer().getPluginManager().isPluginEnabled("VehicleFramework")) {
return;
}
registrationFeeRegistered = true;
try {
Class.forName("net.tfminecraft.vfbuilders.events.VehicleConstructionCancelEvent");
} catch (ClassNotFoundException | LinkageError e) {
// The listener needs the confirm click and cancel event from VFBuilders 2.1.0.
getLogger().warning("VFBuilders is older than 2.1.0, so vehicle registration fees are off; "
+ "update it to charge them");
return;
}
getServer().getPluginManager().registerEvents(new net.tfminecraft.simplefactions.vehicles.fees
.VehicleRegistrationFeeListener(vehicleFeeStore, vehicleFeeConfirmations, this::saveVehicleFees), this);
}

private void registerConstructionFreeze() {
Expand Down Expand Up @@ -622,7 +705,11 @@ private void registerVehicleIntegration() {
getServer().getPluginManager().registerEvents(vehicleMaintenanceRepairListener, this);
getServer().getPluginManager().registerEvents(vehicleSpawnListener, this);
getServer().getPluginManager().registerEvents(battleVehicleEligibilityListener, this);
getServer().getPluginManager().registerEvents(vehicleHandoverListener, this);
getServer().getPluginManager().registerEvents(vehicleReclaimFeeListener, this);
vehicleIntegrationRegistered = true;
// VehicleFramework loads its vehicles as it enables, so wait before reading owners.
getServer().getScheduler().runTaskLater(this, this::recordVehicleOwners, 200L);
if (getServer().getPluginManager().isPluginEnabled("VFBuilders")) {
getLogger().info("[SimpleFactions] VFBuilders vehicle integration enabled");
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public void loadFactions() {
data.laws,
data.governmentData
);
f.getVehicleFeeHandler().load(data.vehicleFees, data.vehicleTypeFees);
if (data.lawChangedAt != null) {
for (Map.Entry<String, Long> entry : data.lawChangedAt.entrySet()) {
LawGroup group = f.getLawHandler().getGroup(entry.getKey());
Expand Down Expand Up @@ -325,6 +326,12 @@ public void saveFaction(Faction f) {
data.dividendTax = f.getTaxHandler().getDividendTax();
data.tariffs = f.getTaxHandler().getTariffs();
data.specificTaxes = serializeSpecificTaxes(f.getTaxHandler());
data.vehicleFees = new HashMap<>(f.getVehicleFeeHandler().serializeRates());
data.vehicleTypeFees = new HashMap<>();
for (Map.Entry<String, Map<String, Double>> entry
: f.getVehicleFeeHandler().serializeTypeRates().entrySet()) {
data.vehicleTypeFees.put(entry.getKey(), new HashMap<>(entry.getValue()));
}
data.capital = f.getCapital();
data.extraNodeCapacity = (double) f.getExtraNodeCapacity();

Expand Down Expand Up @@ -398,6 +405,7 @@ public void saveFaction(Faction f) {
gd.dividendPercent = g.getDividendPercent();
gd.dividendEligible = g.getDividendEligibleSnapshot();
gd.casinoProfit = g.getLedger().getCasinoProfit();
gd.vehicleFeeIncome = g.getLedger().getVehicleFeeIncome();
Map<String, Double> citizenTaxes = g.getLedger().getCitizenTaxesCopy();
gd.citizenTaxes = citizenTaxes.isEmpty() ? null : citizenTaxes;
gd.company = g.getCompany() != null ? g.getCompany().serialize() : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public class FactionData {
@SerializedName("specific taxes")
public HashMap<String, HashMap<String, Double>> specificTaxes = new HashMap<>();

@SerializedName("vehicle fees")
public HashMap<String, Double> vehicleFees;

@SerializedName("vehicle type fees")
public HashMap<String, HashMap<String, Double>> vehicleTypeFees;

public Integer capital;

@SerializedName("extra node capacity")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class GuildData {

@SerializedName("casino profit")
public Double casinoProfit;
public Double vehicleFeeIncome;

@SerializedName("citizen taxes")
public Map<String, Double> citizenTaxes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

public class ProposalData {
public String proposer;
public String type; // "law", "tax", or "political"
public String type; // "law", "tax", "fee", or "political"

// For law proposals
public String groupId;
Expand All @@ -20,6 +20,17 @@ public class ProposalData {
@SerializedName("new tax")
public Double newTax;

// For fee proposals
@SerializedName("fee kind")
public String feeKind;

/** Null for the general rate. */
@SerializedName("fee vehicle")
public String feeVehicle;

@SerializedName("new fee")
public Double newFee;

// For political action proposals
@SerializedName("action key")
public String actionKey;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ public enum Brackets {
GUILD_TAX("Guild Tax"),
VASSAL_TAX("Vassal Tax"),
DIVIDEND_TAX("Dividend Tax"),
TARIFFS("Tariffs");
TARIFFS("Tariffs"),
VEHICLE_TAX("Vehicle Tax (% of upkeep)"),
REGISTRATION_FEE("Registration Fee (x upkeep)"),
TRANSFER_FEE("Transfer Fee (x upkeep)");

private final String display;

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/net/tfminecraft/simplefactions/enums/Rules.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public enum Rules {
GUILD_TAX("Can Collect Guild Taxes", true), //Implemented
DIVIDEND_TAX("Can Collect Dividend Taxes", true), //Implemented
TARIFFS("Can Impose Tariffs", true), //Implemented
VEHICLE_TAX("Can Collect Vehicle Tax", true), //Implemented
REGISTRATION_FEE("Can Charge Vehicle Registration Fees", true), //Implemented
TRANSFER_FEE("Can Charge Vehicle Transfer Fees", true), //Implemented
VASSAL_VOTING_RIGHTS("Vassals Have Voting Rights", true), //Implemented
CAN_FAVOUR("Can Favour Guilds and Vassals", true),
CAN_REPRESS("Can Repress Guilds and Vassals", true),
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/net/tfminecraft/simplefactions/enums/SFGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public enum SFGUI {
LAW_PROPOSAL_SELECT,
PROPOSALS,
SPECIFIC_TAX_PROPOSAL_VIEW,
FEE_PROPOSAL_VIEW,
FEE_CATEGORY_VIEW,
FEE_VEHICLE_VIEW,
LEDGER_VIEW,
COUNCIL_VIEW,
COUNCIL_SELECT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,11 @@ private ProposalData serializeProposal(Proposal p) {
data.taxTarget = tax.getTarget().name();
data.taxId = tax.getId();
data.newTax = tax.getNewTax();
} else if (p.isFeeProposal()) {
data.type = "fee";
data.feeKind = p.getFeeChange().getKind().name();
data.feeVehicle = p.getFeeChange().getVehicleTypeId();
data.newFee = p.getFeeChange().getNewRate();
} else if (p.isPoliticalActionProposal() && p.getPoliticalAction() != null) {
data.type = "political";
data.actionKey = p.getPoliticalAction().getAction().toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import net.tfminecraft.simplefactions.loaders.LawLoader;
import net.tfminecraft.simplefactions.government.Government;
import net.tfminecraft.simplefactions.government.proposal.FeeChange;
import net.tfminecraft.simplefactions.government.proposal.FeeKind;
import net.tfminecraft.simplefactions.government.proposal.Proposal;
import net.tfminecraft.simplefactions.government.proposal.TaxLawChange;
import net.tfminecraft.simplefactions.laws.Law;
Expand All @@ -30,6 +32,9 @@ public ProposalHandler(Government gov, boolean movement) {
}

private List<Proposal> proposals = new ArrayList<>();

// Stands in for the null vehicle type of a general fee in the saved form.
private static final String ALL_VEHICLES = "*";

public boolean canPropose(String member) {
if(movement) return true;
Expand All @@ -54,6 +59,10 @@ public boolean canBeProposed(Proposal proposal) {
LawGroup g = gov.getFaction().getLawHandler().getGroup(p.getLaw().getGroup());
if(g.getId().equalsIgnoreCase(group.getId())) return false;
}
} else if(proposal.isFeeProposal()) {
for(Proposal p : proposals) {
if(p.isFeeProposal() && p.getFeeChange().sameTarget(proposal.getFeeChange())) return false;
}
} else if(proposal.isTaxProposal()) {
TaxLawChange change = proposal.getTaxChange();
for(Proposal p : proposals) {
Expand Down Expand Up @@ -94,6 +103,10 @@ public List<String> serializeProposals() {
} else if (p.isTaxProposal() && p.getTaxChange() != null) {
TaxLawChange tax = p.getTaxChange();
result.add(p.getProposer() + ":tax:" + tax.getTarget().name() + ":" + tax.getId() + ":" + tax.getNewTax());
} else if (p.isFeeProposal()) {
FeeChange fee = p.getFeeChange();
result.add(p.getProposer() + ":fee:" + fee.getKind().name() + ":"
+ (fee.isGeneral() ? ALL_VEHICLES : fee.getVehicleTypeId()) + ":" + fee.getNewRate());
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
return result;
Expand All @@ -120,6 +133,23 @@ public void restoreProposals(net.tfminecraft.simplefactions.objects.Faction fact
}
}
}
} else if (s.startsWith("fee:")) {
// kind:type:rate, where the type may itself contain colons.
String body = s.substring(4);
int first = body.indexOf(':');
int last = body.lastIndexOf(':');
if (first > 0 && last > first) {
try {
FeeKind kind = FeeKind.valueOf(body.substring(0, first));
String typeField = body.substring(first + 1, last);
String type = ALL_VEHICLES.equals(typeField) ? null : typeField;
Proposal p = new Proposal(proposer, gov);
p.setFeeProposal(new FeeChange(kind, type, Double.parseDouble(body.substring(last + 1))));
proposals.add(p);
} catch (Exception e) {
// Skip malformed proposals
}
}
} else if (s.startsWith("tax:")) {
String[] parts = s.substring(4).split(":");
if (parts.length >= 3) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,12 @@ private Proposal deserializeProposal(Faction faction, ProposalData proposalData)
new net.tfminecraft.simplefactions.government.proposal.TaxLawChange(target, proposalData.taxId, proposalData.newTax);
p.setTaxProposal(tax);
return p;
} else if ("fee".equals(proposalData.type)) {
net.tfminecraft.simplefactions.government.proposal.FeeKind kind =
net.tfminecraft.simplefactions.government.proposal.FeeKind.valueOf(proposalData.feeKind);
p.setFeeProposal(new net.tfminecraft.simplefactions.government.proposal.FeeChange(
kind, proposalData.feeVehicle, proposalData.newFee == null ? 0.0 : proposalData.newFee));
return p;
} else if ("political".equals(proposalData.type)) {
Action action = Action.valueOf(proposalData.actionKey);
PoliticalAction politicalAction = new PoliticalAction(action);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ private static boolean isLawOrTax(Cause cause) {
return true;
}
Proposal proposal = cause.getProposal();
if (proposal != null && (proposal.isLawProposal() || proposal.isTaxProposal())) {
if (proposal != null && (proposal.isLawProposal() || proposal.isTaxProposal() || proposal.isFeeProposal())) {
return true;
}
Action fromProposal = actionOf(cause);
Expand Down
Loading