First try at a tax system

This hasn't been tested. This is a tax system where Factions can tax their players. Later it should be extended do that servers can tax factions based on how much land they have claimed. We start of with this simpler system to make sure that can be tested in real-life environments before we go ahead with the more full fledged system.
This commit is contained in:
Magnus Ulf 2019-07-15 07:55:12 +02:00
parent 193a4acc83
commit 35c38ce4b8
13 changed files with 416 additions and 93 deletions

View File

@ -71,7 +71,7 @@ import com.massivecraft.factions.integration.placeholderapi.IntegrationPlacehold
import com.massivecraft.factions.integration.venturechat.IntegrationVentureChat; import com.massivecraft.factions.integration.venturechat.IntegrationVentureChat;
import com.massivecraft.factions.integration.worldguard.IntegrationWorldGuard; import com.massivecraft.factions.integration.worldguard.IntegrationWorldGuard;
import com.massivecraft.factions.mixin.PowerMixin; import com.massivecraft.factions.mixin.PowerMixin;
import com.massivecraft.factions.task.TaskEconLandReward; import com.massivecraft.factions.task.TaskTax;
import com.massivecraft.factions.task.TaskFlagPermCreate; import com.massivecraft.factions.task.TaskFlagPermCreate;
import com.massivecraft.factions.task.TaskPlayerPowerUpdate; import com.massivecraft.factions.task.TaskPlayerPowerUpdate;
import com.massivecraft.massivecore.MassivePlugin; import com.massivecraft.massivecore.MassivePlugin;
@ -203,7 +203,7 @@ public class Factions extends MassivePlugin
public List<Class<?>> getClassesActiveTasks() public List<Class<?>> getClassesActiveTasks()
{ {
return MUtil.list( return MUtil.list(
TaskEconLandReward.class, TaskTax.class,
TaskFlagPermCreate.class, TaskFlagPermCreate.class,
TaskPlayerPowerUpdate.class TaskPlayerPowerUpdate.class
); );

View File

@ -51,6 +51,7 @@ public class CmdFactions extends FactionsCommand
public CmdFactionsRelationOld cmdFactionsRelationOldTruce = new CmdFactionsRelationOld("truce"); public CmdFactionsRelationOld cmdFactionsRelationOldTruce = new CmdFactionsRelationOld("truce");
public CmdFactionsRelationOld cmdFactionsRelationOldNeutral = new CmdFactionsRelationOld("neutral"); public CmdFactionsRelationOld cmdFactionsRelationOldNeutral = new CmdFactionsRelationOld("neutral");
public CmdFactionsRelationOld cmdFactionsRelationOldEnemy = new CmdFactionsRelationOld("enemy"); public CmdFactionsRelationOld cmdFactionsRelationOldEnemy = new CmdFactionsRelationOld("enemy");
public CmdFactionsTax cmdFactionsTax = new CmdFactionsTax();
public CmdFactionsPerm cmdFactionsPerm = new CmdFactionsPerm(); public CmdFactionsPerm cmdFactionsPerm = new CmdFactionsPerm();
public CmdFactionsFlag cmdFactionsFlag = new CmdFactionsFlag(); public CmdFactionsFlag cmdFactionsFlag = new CmdFactionsFlag();
public CmdFactionsFly cmdFactionsFly = new CmdFactionsFly(); public CmdFactionsFly cmdFactionsFly = new CmdFactionsFly();

View File

@ -51,7 +51,7 @@ public class CmdFactionsMoneyconvert extends FactionsCommand
" This command allows to convert to the new system where the money of a Faction" + " This command allows to convert to the new system where the money of a Faction" +
" is stored within the Factions plugin. Then all economy plugins can be used with Factions."); " is stored within the Factions plugin. Then all economy plugins can be used with Factions.");
} }
ConfirmationUtil.tryConfirm(this); ConfirmationUtil.tryConfirm(this);
MConf.get().useNewMoneySystem = true; MConf.get().useNewMoneySystem = true;

View File

@ -0,0 +1,11 @@
package com.massivecraft.factions.cmd;
public class CmdFactionsTax extends FactionsCommand
{
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
}

View File

@ -0,0 +1,49 @@
package com.massivecraft.factions.cmd;
import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.task.TaskTax;
import com.massivecraft.massivecore.MassiveException;
import com.massivecraft.massivecore.money.Money;
import com.massivecraft.massivecore.util.TimeDiffUtil;
import com.massivecraft.massivecore.util.TimeUnit;
import com.massivecraft.massivecore.util.Txt;
import java.util.LinkedHashMap;
public class CmdFactionsTaxInfo extends FactionsCommand
{
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public CmdFactionsTaxInfo()
{
}
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public void perform() throws MassiveException
{
if (!TaskTax.get().areTaxesEnabled())
{
throw new MassiveException().addMsg("<b>Tax is not enabled on this server.");
}
LinkedHashMap<TimeUnit, Long> timeUnitcounts = TimeDiffUtil.limit(TimeDiffUtil.unitcounts(MConf.get().taxTaskPeriodMillis, TimeUnit.getAll()), 3);
String periodString = TimeDiffUtil.formatedVerboose(timeUnitcounts);
msg("<key>Taxation Period: <i>every %s<i>.", periodString);
long nextTaxationTime = MConf.get().taxTaskPeriodMillis + MConf.get().taxTaskPeriodMillis;
msg("<key>Next Taxation: %s", Txt.getTimeDeltaDescriptionRelNow(nextTaxationTime));
String minTax = Money.format(MConf.get().taxPlayerMinimum);
String maxTax = Money.format(MConf.get().taxPlayerMaximum);
msg("<i>Taxes for players can be set between <reset>%s <i>and <reset>%s<i>.", minTax, maxTax);
}
}

View File

@ -11,6 +11,8 @@ import com.massivecraft.factions.predicate.PredicateCommandSenderFaction;
import com.massivecraft.factions.predicate.PredicateMPlayerRank; import com.massivecraft.factions.predicate.PredicateMPlayerRank;
import com.massivecraft.factions.util.MiscUtil; import com.massivecraft.factions.util.MiscUtil;
import com.massivecraft.factions.util.RelationUtil; import com.massivecraft.factions.util.RelationUtil;
import com.massivecraft.massivecore.Couple;
import com.massivecraft.massivecore.Identified;
import com.massivecraft.massivecore.collections.MassiveList; import com.massivecraft.massivecore.collections.MassiveList;
import com.massivecraft.massivecore.collections.MassiveMap; import com.massivecraft.massivecore.collections.MassiveMap;
import com.massivecraft.massivecore.collections.MassiveMapDef; import com.massivecraft.massivecore.collections.MassiveMapDef;
@ -38,6 +40,7 @@ import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -158,8 +161,13 @@ public class Faction extends Entity<Faction> implements FactionsParticipator, MP
// Null means default. // Null means default.
private MassiveMapDef<String, Boolean> flags = new MassiveMapDef<>(); private MassiveMapDef<String, Boolean> flags = new MassiveMapDef<>();
private Map<String, Set<String>> perms = this.createNewPermMap(); private Map<String, Set<String>> perms = this.createNewPermMap();
// What is the base tax on members of the faction?
// Specific taxes on ranks or players.
public static String IDENTIFIER_TAX_BASE = "base";
private Map<String, Double> tax = new MassiveMap<>();
// -------------------------------------------- // // -------------------------------------------- //
// FIELD: id // FIELD: id
@ -944,6 +952,62 @@ public class Faction extends Entity<Faction> implements FactionsParticipator, MP
return MPermColl.get().getFixed(permId) != null; return MPermColl.get().getFixed(permId) != null;
} }
// -------------------------------------------- //
// FIELD: tax
// -------------------------------------------- //
// RAW
public Map<String, Double> getTax() { return this.tax; }
// FINER GET
public Double getTaxFor(String id)
{
if (id == null) throw new NullPointerException("id");
return this.tax.get(id);
}
public Double getTaxFor(Identified identified)
{
if (identified == null) throw new NullPointerException("identified");
return this.getTaxFor(identified.getId());
}
public double getTaxForPlayer(MPlayer mplayer)
{
return getTaxAndReasonForPlayer(mplayer).map(Entry::getValue).orElse(0D);
}
public Optional<Entry<String, Double>> getTaxAndReasonForPlayer(MPlayer mplayer)
{
if (mplayer == null) throw new NullPointerException("mplayer");
if (mplayer.getFaction() != this) throw new IllegalArgumentException("Player " + mplayer.getId() + " not in " + this.getId());
Double ret = null;
ret = this.getTaxFor(mplayer);
if (ret != null) return Optional.of(new Couple<>(mplayer.getId(), ret));
ret = this.getTaxFor(mplayer.getRank());
if (ret != null) return Optional.of(new Couple<>(mplayer.getRank().getId(), ret));
ret = this.getTaxFor(IDENTIFIER_TAX_BASE);
if (ret != null) return Optional.of(new Couple<>(IDENTIFIER_TAX_BASE, ret));
return Optional.empty();
}
// FINER SET
public Double setTaxFor(String id, Double value)
{
return this.tax.put(id, value);
}
public Double setTaxFor(Identified identified, Double value)
{
return this.setTaxFor(identified.getId(), value);
}
// -------------------------------------------- // // -------------------------------------------- //
// OVERRIDE: RelationParticipator // OVERRIDE: RelationParticipator
// -------------------------------------------- // // -------------------------------------------- //

View File

@ -2,7 +2,6 @@ package com.massivecraft.factions.entity;
import com.massivecraft.factions.Factions; import com.massivecraft.factions.Factions;
import com.massivecraft.factions.Rel; import com.massivecraft.factions.Rel;
import com.massivecraft.factions.integration.Econ;
import com.massivecraft.factions.util.MiscUtil; import com.massivecraft.factions.util.MiscUtil;
import com.massivecraft.massivecore.collections.MassiveMap; import com.massivecraft.massivecore.collections.MassiveMap;
import com.massivecraft.massivecore.store.Coll; import com.massivecraft.massivecore.store.Coll;
@ -165,48 +164,9 @@ public class FactionColl extends Coll<Faction>
} }
// -------------------------------------------- // // -------------------------------------------- //
// LAND REWARD // NAME
// -------------------------------------------- // // -------------------------------------------- //
public void econLandRewardRoutine()
{
// If econ is enabled ...
if (!Econ.isEnabled()) return;
// ... and the land reward is non zero ...
double econLandReward = MConf.get().econLandReward;
if (econLandReward == 0.0) return;
// ... log initiation ...
Factions.get().log("Running econLandRewardRoutine...");
MFlag flagPeaceful = MFlag.getFlagPeaceful();
// ... and for each faction ...
for (Faction faction : this.getAll())
{
// ... get the land count ...
int landCount = faction.getLandCount();
// ... and if the faction isn't peaceful and has land ...
if (landCount == 0 || faction.getFlag(flagPeaceful)) continue;
// ... get the faction's members ...
List<MPlayer> players = faction.getMPlayers();
// ... calculate the reward ...
int playerCount = players.size();
double reward = econLandReward * landCount / playerCount;
// ... and grant the reward.
String description = String.format("own %s faction land divided among %s members", landCount, playerCount);
for (MPlayer player : players)
{
Econ.modifyMoney(player, reward, description);
}
}
}
@Override @Override
public Faction getByName(String name) public Faction getByName(String name)
{ {

View File

@ -30,7 +30,7 @@ public class MConf extends Entity<MConf>
// -------------------------------------------- // // -------------------------------------------- //
// META // META
// -------------------------------------------- // // -------------------------------------------- //
protected static transient MConf i; protected static transient MConf i;
public static MConf get() { return i; } public static MConf get() { return i; }
@ -487,7 +487,36 @@ public class MConf extends Entity<MConf>
public boolean logFactionLeave = true; public boolean logFactionLeave = true;
public boolean logLandClaims = true; public boolean logLandClaims = true;
public boolean logMoneyTransactions = true; public boolean logMoneyTransactions = true;
// -------------------------------------------- //
// TAX
// -------------------------------------------- //
// Should the tax system be enabled?
public boolean taxEnabled = false;
// How much can you tax a player?
public double taxPlayerMinimum = -10;
public double taxPlayerMaximum = 10;
// How much should Factions be taxed?
//public double taxUpkeepBase = 0;
//public double taxUpkeepPerChunk = 1;
// When is a player inactive?
public int taxInactiveDays = 3;
// When the last run time was (in unix time)
// 0 means never
public long taxTaskLastMillis = 0;
// Tax run when?
// 0 means at midnight UTC it can be offset by a certain millis
public long taxTaskInvocationOffsetMillis = 0;
// How often should the task be run?
public long taxTaskPeriodMillis = TimeUnit.MILLIS_PER_DAY;
// -------------------------------------------- // // -------------------------------------------- //
// ENUMERATIONS // ENUMERATIONS
// -------------------------------------------- // // -------------------------------------------- //
@ -574,10 +603,6 @@ public class MConf extends Entity<MConf>
// This requires that you have the external plugin called "Vault" installed. // This requires that you have the external plugin called "Vault" installed.
public boolean econEnabled = true; public boolean econEnabled = true;
// A money reward per chunk. This reward is divided among the players in the faction.
// You set the time inbetween each reward almost at the top of this config file. (taskEconLandRewardMinutes)
public double econLandReward = 0.00;
// When paying a cost you may specify an account that should receive the money here. // When paying a cost you may specify an account that should receive the money here.
// Per default "" the money is just destroyed. // Per default "" the money is just destroyed.
public String econUniverseAccount = ""; public String econUniverseAccount = "";

View File

@ -36,6 +36,7 @@ public class MFlag extends Entity<MFlag> implements Prioritized, Registerable, N
public final static transient String ID_PEACEFUL = "peaceful"; public final static transient String ID_PEACEFUL = "peaceful";
public final static transient String ID_INFPOWER = "infpower"; public final static transient String ID_INFPOWER = "infpower";
public final static transient String ID_FLY = "fly"; public final static transient String ID_FLY = "fly";
public final static transient String ID_TAXKICK = "taxkick";
public final static transient int PRIORITY_OPEN = 1_000; public final static transient int PRIORITY_OPEN = 1_000;
public final static transient int PRIORITY_MONSTERS = 2_000; public final static transient int PRIORITY_MONSTERS = 2_000;
@ -53,6 +54,7 @@ public class MFlag extends Entity<MFlag> implements Prioritized, Registerable, N
public final static transient int PRIORITY_PEACEFUL = 14_000; public final static transient int PRIORITY_PEACEFUL = 14_000;
public final static transient int PRIORITY_INFPOWER = 15_000; public final static transient int PRIORITY_INFPOWER = 15_000;
public final static transient int PRIORITY_FLY = 16_000; public final static transient int PRIORITY_FLY = 16_000;
public final static transient int PRIORITY_TAXKICK = 17_000;
// -------------------------------------------- // // -------------------------------------------- //
// META: CORE // META: CORE
@ -93,6 +95,7 @@ public class MFlag extends Entity<MFlag> implements Prioritized, Registerable, N
getFlagPeaceful(); getFlagPeaceful();
getFlagInfpower(); getFlagInfpower();
getFlagFly(); getFlagFly();
getFlagTaxKick();
} }
public static MFlag getFlagOpen() { return getCreative(PRIORITY_OPEN, ID_OPEN, ID_OPEN, "Can the faction be joined without an invite?", "Anyone can join. No invite required.", "An invite is required to join.", false, true, true); } public static MFlag getFlagOpen() { return getCreative(PRIORITY_OPEN, ID_OPEN, ID_OPEN, "Can the faction be joined without an invite?", "Anyone can join. No invite required.", "An invite is required to join.", false, true, true); }
@ -111,6 +114,7 @@ public class MFlag extends Entity<MFlag> implements Prioritized, Registerable, N
public static MFlag getFlagPeaceful() { return getCreative(PRIORITY_PEACEFUL, ID_PEACEFUL, ID_PEACEFUL, "Is the faction in truce with everyone?", "The faction is in truce with everyone.", "The faction relations work as usual.", false, false, true); } public static MFlag getFlagPeaceful() { return getCreative(PRIORITY_PEACEFUL, ID_PEACEFUL, ID_PEACEFUL, "Is the faction in truce with everyone?", "The faction is in truce with everyone.", "The faction relations work as usual.", false, false, true); }
public static MFlag getFlagInfpower() { return getCreative(PRIORITY_INFPOWER, ID_INFPOWER, ID_INFPOWER, "Does the faction have infinite power?", "The faction has infinite power.", "The faction power works as usual.", false, false, true); } public static MFlag getFlagInfpower() { return getCreative(PRIORITY_INFPOWER, ID_INFPOWER, ID_INFPOWER, "Does the faction have infinite power?", "The faction has infinite power.", "The faction power works as usual.", false, false, true); }
public static MFlag getFlagFly() { return getCreative(PRIORITY_FLY, ID_FLY, ID_FLY, "Is flying allowed for members in faction territory?", "Members can fly in faction territory.", "Members can not fly in faction territory.", false, false, true); } public static MFlag getFlagFly() { return getCreative(PRIORITY_FLY, ID_FLY, ID_FLY, "Is flying allowed for members in faction territory?", "Members can fly in faction territory.", "Members can not fly in faction territory.", false, false, true); }
public static MFlag getFlagTaxKick() { return getCreative(PRIORITY_TAXKICK, ID_TAXKICK, ID_TAXKICK, "Are players kicked for not paying taxes?", "Members are kicked for not paying taxes.", "Members are not kicked for not paying taxes.", false, true, true); }
public static MFlag getCreative(int priority, String id, String name, String desc, String descYes, String descNo, boolean standard, boolean editable, boolean visible) public static MFlag getCreative(int priority, String id, String name, String desc, String descYes, String descNo, boolean standard, boolean editable, boolean visible)
{ {

View File

@ -56,9 +56,10 @@ public class MPerm extends Entity<MPerm> implements Prioritized, Registerable, N
public final static transient String ID_WITHDRAW = "withdraw"; public final static transient String ID_WITHDRAW = "withdraw";
public final static transient String ID_TERRITORY = "territory"; public final static transient String ID_TERRITORY = "territory";
public final static transient String ID_ACCESS = "access"; public final static transient String ID_ACCESS = "access";
public final static transient String ID_VOTE = "VOTE"; public final static transient String ID_VOTE = "VOTE"; // Why is this capitalised? Can that be easily changed?
public final static transient String ID_CREATEVOTE = "createvote"; public final static transient String ID_CREATEVOTE = "createvote";
public final static transient String ID_CLAIMNEAR = "claimnear"; public final static transient String ID_CLAIMNEAR = "claimnear";
public final static transient String ID_TAX = "tax";
public final static transient String ID_REL = "rel"; public final static transient String ID_REL = "rel";
public final static transient String ID_DISBAND = "disband"; public final static transient String ID_DISBAND = "disband";
public final static transient String ID_FLAGS = "flags"; public final static transient String ID_FLAGS = "flags";
@ -87,6 +88,7 @@ public class MPerm extends Entity<MPerm> implements Prioritized, Registerable, N
public final static transient int PRIORITY_VOTE = 18200; public final static transient int PRIORITY_VOTE = 18200;
public final static transient int PRIORITY_CREATEVOTE = 18600; public final static transient int PRIORITY_CREATEVOTE = 18600;
public final static transient int PRIORITY_CLAIMNEAR = 19000; public final static transient int PRIORITY_CLAIMNEAR = 19000;
public final static transient int PRIORITY_TAX = 19500;
public final static transient int PRIORITY_REL = 20000; public final static transient int PRIORITY_REL = 20000;
public final static transient int PRIORITY_DISBAND = 21000; public final static transient int PRIORITY_DISBAND = 21000;
public final static transient int PRIORITY_FLAGS = 22000; public final static transient int PRIORITY_FLAGS = 22000;
@ -139,6 +141,7 @@ public class MPerm extends Entity<MPerm> implements Prioritized, Registerable, N
getPermVote(); getPermVote();
getPermCreateVote(); getPermCreateVote();
getPermClaimnear(); getPermClaimnear();
getPermTax();
getPermRel(); getPermRel();
getPermDisband(); getPermDisband();
getPermFlags(); getPermFlags();
@ -168,6 +171,7 @@ public class MPerm extends Entity<MPerm> implements Prioritized, Registerable, N
public static MPerm getPermVote() { return getCreative(PRIORITY_VOTE, ID_VOTE, ID_VOTE, "vote", MUtil.set("LEADER", "OFFICER", "MEMBER", "RECRUIT"), false, true, true); } public static MPerm getPermVote() { return getCreative(PRIORITY_VOTE, ID_VOTE, ID_VOTE, "vote", MUtil.set("LEADER", "OFFICER", "MEMBER", "RECRUIT"), false, true, true); }
public static MPerm getPermCreateVote() { return getCreative(PRIORITY_CREATEVOTE, ID_CREATEVOTE, ID_CREATEVOTE, "manage votes", MUtil.set("LEADER", "OFFICER"), false, true, true); } public static MPerm getPermCreateVote() { return getCreative(PRIORITY_CREATEVOTE, ID_CREATEVOTE, ID_CREATEVOTE, "manage votes", MUtil.set("LEADER", "OFFICER"), false, true, true); }
public static MPerm getPermClaimnear() { return getCreative(PRIORITY_CLAIMNEAR, ID_CLAIMNEAR, ID_CLAIMNEAR, "claim nearby", MUtil.set("LEADER", "OFFICER", "MEMBER", "RECRUIT", "ALLY"), false, false, false); } // non editable, non visible. public static MPerm getPermClaimnear() { return getCreative(PRIORITY_CLAIMNEAR, ID_CLAIMNEAR, ID_CLAIMNEAR, "claim nearby", MUtil.set("LEADER", "OFFICER", "MEMBER", "RECRUIT", "ALLY"), false, false, false); } // non editable, non visible.
public static MPerm getPermTax() { return getCreative(PRIORITY_TAX, ID_TAX, ID_TAX, "set taxes", MUtil.set("LEADER"), false, true, true); }
public static MPerm getPermRel() { return getCreative(PRIORITY_REL, ID_REL, ID_REL, "change relations", MUtil.set("LEADER", "OFFICER"), false, true, true); } public static MPerm getPermRel() { return getCreative(PRIORITY_REL, ID_REL, ID_REL, "change relations", MUtil.set("LEADER", "OFFICER"), false, true, true); }
public static MPerm getPermDisband() { return getCreative(PRIORITY_DISBAND, ID_DISBAND, ID_DISBAND, "disband the faction", MUtil.set("LEADER"), false, true, true); } public static MPerm getPermDisband() { return getCreative(PRIORITY_DISBAND, ID_DISBAND, ID_DISBAND, "disband the faction", MUtil.set("LEADER"), false, true, true); }
public static MPerm getPermFlags() { return getCreative(PRIORITY_FLAGS, ID_FLAGS, ID_FLAGS, "manage flags", MUtil.set("LEADER"), false, true, true); } public static MPerm getPermFlags() { return getCreative(PRIORITY_FLAGS, ID_FLAGS, ID_FLAGS, "manage flags", MUtil.set("LEADER"), false, true, true); }

View File

@ -215,6 +215,11 @@ public class Econ
} }
} }
public static boolean hasAtLeast(EconomyParticipator ep, double delta)
{
return hasAtLeast(ep, delta, null);
}
public static boolean hasAtLeast(EconomyParticipator ep, double delta, String toDoThis) public static boolean hasAtLeast(EconomyParticipator ep, double delta, String toDoThis)
{ {
if ( ! isEnabled()) return true; if ( ! isEnabled()) return true;
@ -287,6 +292,11 @@ public class Econ
} }
public static boolean moveMoney(EconomyParticipator from, EconomyParticipator to, EconomyParticipator by, double amount) public static boolean moveMoney(EconomyParticipator from, EconomyParticipator to, EconomyParticipator by, double amount)
{
return moveMoney(from, to, by, amount, "Factions");
}
public static boolean moveMoney(EconomyParticipator from, EconomyParticipator to, EconomyParticipator by, double amount, String category)
{ {
final boolean fromFaction = from instanceof Faction; final boolean fromFaction = from instanceof Faction;
final boolean toFaction = to instanceof Faction; final boolean toFaction = to instanceof Faction;
@ -294,13 +304,13 @@ public class Econ
// If the old money system is used just do that // If the old money system is used just do that
if (!MConf.get().useNewMoneySystem) if (!MConf.get().useNewMoneySystem)
{ {
return Money.move(from, to, by, amount, "Factions"); return Money.move(from, to, by, amount, category);
} }
// Or if neither to or from is a faction // Or if neither to or from is a faction
if (!fromFaction && !toFaction) if (!fromFaction && !toFaction)
{ {
return Money.move(from, to, by, amount, "Factions"); return Money.move(from, to, by, amount, category);
} }
// Handle from // Handle from

View File

@ -1,39 +0,0 @@
package com.massivecraft.factions.task;
import com.massivecraft.factions.entity.FactionColl;
import com.massivecraft.factions.entity.MConf;
import com.massivecraft.massivecore.MassiveCore;
import com.massivecraft.massivecore.ModuloRepeatTask;
import com.massivecraft.massivecore.util.TimeUnit;
public class TaskEconLandReward extends ModuloRepeatTask
{
// -------------------------------------------- //
// INSTANCE
// -------------------------------------------- //
private static TaskEconLandReward i = new TaskEconLandReward();
public static TaskEconLandReward get() { return i; }
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public long getDelayMillis()
{
// The interval is determined by the MConf rather than being set with setDelayMillis.
return (long) (MConf.get().taskEconLandRewardMinutes * TimeUnit.MILLIS_PER_MINUTE);
}
@Override
public void invoke(long now)
{
// If this is the task server ...
if (!MassiveCore.isTaskServer()) return;
// ... process the econ land rewards.
FactionColl.get().econLandRewardRoutine();
}
}

View File

@ -0,0 +1,234 @@
package com.massivecraft.factions.task;
import com.massivecraft.factions.Factions;
import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.FactionColl;
import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.entity.MFlag;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.factions.entity.MPlayerColl;
import com.massivecraft.factions.event.EventFactionsMembershipChange;
import com.massivecraft.factions.event.EventFactionsMembershipChange.MembershipChangeReason;
import com.massivecraft.factions.integration.Econ;
import com.massivecraft.massivecore.Couple;
import com.massivecraft.massivecore.Engine;
import com.massivecraft.massivecore.MassiveCore;
import com.massivecraft.massivecore.collections.MassiveMap;
import com.massivecraft.massivecore.mixin.MixinMessage;
import com.massivecraft.massivecore.money.Money;
import com.massivecraft.massivecore.util.IdUtil;
import com.massivecraft.massivecore.util.TimeUnit;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TaskTax extends Engine
{
// -------------------------------------------- //
// INSTANCE
// -------------------------------------------- //
private static TaskTax i = new TaskTax();
public static TaskTax get() { return i; }
public TaskTax()
{
// Just check once a minute
this.setPeriod(60L * 20L);
}
// -------------------------------------------- //
// OVERRIDE: RUNNABLE
// -------------------------------------------- //
@Override
public void run()
{
final long now = System.currentTimeMillis();
MixinMessage.get().msgAll("<i>Running TaskTax");
// If this is the right server ...
if ( ! MassiveCore.isTaskServer()) return;
MixinMessage.get().msgAll("<i>Task server");
// ... and taxing is enabled ...
if ( ! this.areTaxesEnabled()) return;
MixinMessage.get().msgAll("<i>Taxes enabled");
// ... and the current invocation ...
final long currentInvocation = getInvocationFromMillis(now);
MixinMessage.get().msgAll("<i>currentInvocation: %d", currentInvocation);
// ... and the last invocation ...
final long lastInvocation = getInvocationFromMillis(MConf.get().taxTaskLastMillis);
MixinMessage.get().msgAll("<i>lastInvocation: %d", lastInvocation);
// ... are different ...
if (currentInvocation == lastInvocation) return;
// ... then it's time to invoke.
invoke(now);
}
public boolean areTaxesEnabled()
{
return Econ.isEnabled() && MConf.get().taxEnabled;
}
public void invoke(long now)
{
taxPlayers(now);
taxFactions(now);
}
public void taxPlayers(long now)
{
MixinMessage.get().msgAll("<i>Taxation of players starting.");
long start = System.nanoTime();
// Tax players and track how many players are taxed and how much
Map<Faction, Couple<Integer, Double>> faction2tax = new MassiveMap<>();
List<Couple<MPlayer, Double>> taxes = MPlayerColl.get().getAll().stream()
.filter(mp -> shouldBeTaxed(now, mp))
.map(mp -> new Couple<>(mp, getTax(mp)))
.filter(e -> e.getValue() != 0D)
.collect(Collectors.toList());
String debug = taxes.stream()
.map(c -> c.getFirst().getName() + ": " + c.getSecond())
.reduce((s1, s2) -> s1 + "\n" + s2).orElse("");
MixinMessage.get().messageAll(debug);
// Pay the highest taxes first.
// That way taxes are collected before wages are given.
Comparator<Couple<MPlayer, Double>> comparator = Comparator.comparingDouble(Couple::getSecond);
comparator = comparator.reversed();
taxes.sort(comparator);
for (Couple<MPlayer, Double> couple : taxes)
{
double tax = doTaxPlayer(couple);
if (tax == 0D) continue;
// Log data
Faction faction = couple.getFirst().getFaction();
Couple<Integer, Double> newCouple = new Couple<>(1, tax);
faction2tax.merge(faction, newCouple,
(c1, c2) -> new Couple<>(c1.getFirst() + c2.getFirst(), c1.getSecond() + c2.getSecond()));
}
// Inform factions
faction2tax.forEach(this::informFactionOfPlayerTax);
// Infrom of taxation complete
int count = faction2tax.values().stream().mapToInt(Couple::getFirst).sum();
MixinMessage.get().msgAll("<i>Taxation of players complete. <h>%d <i>players were taxed.", count);
long end = System.nanoTime();
double elapsedSeconds = (end - start) / 1000_000_000D;
MixinMessage.get().msgAll("<i>Took <h>%.2f <i>seconds.", elapsedSeconds);
}
private double getTax(MPlayer mplayer)
{
return mplayer.getFaction().getTaxForPlayer(mplayer);
}
private double doTaxPlayer(Couple<MPlayer, Double> couple)
{
return doTaxPlayer(couple.getFirst(), couple.getSecond());
}
private double doTaxPlayer(MPlayer mplayer, double tax)
{
Faction faction = mplayer.getFaction();
boolean success = Econ.moveMoney(mplayer, faction, null, tax, "Factions Tax");
if (success)
{
// Inform player
if (mplayer.isOnline())
{
if (tax > 0) mplayer.msg("<i>You were just taxed <reset>%s <i> by your faction.", Money.format(tax)); // Tax
else mplayer.msg("<i>You were just paid <reset>%s <i> by your faction.", Money.format(-tax)); // Salary
}
return tax;
}
else if (tax > 0) // If a tax
{
faction.msg("%s<i> couldn't afford tax!", mplayer.describeTo(faction));
boolean kicked = tryKickPlayer(mplayer);
if (!kicked) faction.msg("%s <i>could not afford tax.", mplayer.describeTo(faction));
return 0D;
}
else // If a salary
{
faction.msg("<i>Your faction couldn't afford to pay <reset>%s <i>to %s<i>.", Money.format(-tax), mplayer.describeTo(faction));
return 0D;
}
}
private boolean shouldBeTaxed(long now, MPlayer mplayer)
{
// Must have faction
if ( ! mplayer.hasFaction()) return false;
// Must have been online recently
long offlinePeriod;
if (mplayer.isOnline()) offlinePeriod = 0;
else offlinePeriod = now - mplayer.getLastActivityMillis();
int inactiveDays = MConf.get().taxInactiveDays;
if (inactiveDays > 0 && offlinePeriod > inactiveDays * TimeUnit.MILLIS_PER_DAY) return false;
return true;
}
private boolean tryKickPlayer(MPlayer mplayer)
{
Faction faction = mplayer.getFaction();
if (mplayer.getRank().isLeader()) return false;
if ( ! faction.getFlag(MFlag.getFlagTaxKick())) return false;
EventFactionsMembershipChange event = new EventFactionsMembershipChange(null, mplayer, FactionColl.get().getNone(), MembershipChangeReason.KICK);
event.run();
if (event.isCancelled()) return false;
faction.msg("%s <i>could not afford tax and was kicked from your faction.", mplayer.describeTo(faction));
if (MConf.get().logFactionKick)
{
MPlayer console = MPlayer.get(IdUtil.CONSOLE_ID);
Factions.get().log("%s <i>could not afford tax and was kicked from <reset>%s<i>.", mplayer.describeTo(console), faction.describeTo(console));
}
// Apply
faction.uninvite(mplayer);
mplayer.resetFactionData();
return true;
}
private void informFactionOfPlayerTax(Faction faction, Couple<Integer, Double> couple)
{
faction.msg("<i>A total of <h>%d <i>players in your faction were taxed for a total of <reset>%s<i>.", couple.getFirst(), Money.format(couple.getSecond()));
}
public void taxFactions(long now)
{
// TODO
String msg = "<i>For the time being factions themselves cannot be taxed. This feature will be added at a later date.";
MixinMessage.get().msgOne(IdUtil.CONSOLE_ID, msg);
}
// -------------------------------------------- //
// TASK MILLIS AND INVOCATION
// -------------------------------------------- //
// The invocation is the amount of periods from UNIX time to now.
// It will increment by one when a period has passed.
// Remember to check isDisabled first!
// Here we accept millis from inside the period by rounding down.
private static long getInvocationFromMillis(long millis)
{
return (millis - MConf.get().taxTaskInvocationOffsetMillis) / MConf.get().taxTaskPeriodMillis;
}
}