Add tax commands

This commit is contained in:
Magnus Ulf 2019-08-11 16:12:50 +02:00
parent 46cf009b01
commit cd3b846ae1
11 changed files with 491 additions and 61 deletions

View File

@ -41,6 +41,7 @@ permissions:
factions.documentation.power: {description: show power documentation, default: false}
factions.documentation.perms: {description: show perms documentation, default: false}
factions.documentation.ranks: {description: show rank documentation, default: false}
factions.documentation.tax: {description: show tax documentation, default: false}
factions.documentation.warps: {description: show warp documentation, default: false}
factions.expansions: {description: list expansions, default: false}
factions.faction: {description: show faction information, default: false}
@ -110,6 +111,11 @@ permissions:
factions.seechunkold: {description: see the chunk you stand in, default: false}
factions.setpower: {description: set power, default: false}
factions.status: {description: show status, default: false}
factions.tax: {description: manage taxes, default: false}
factions.tax.faction: {description: show faction tax, default: false}
factions.tax.player: {description: show player tax, default: false}
factions.tax.run: {description: run a tax collection, default: false}
factions.tax.set: {description: set taxes, default: false}
factions.name: {description: set faction name, default: false}
factions.title: {description: set player title, default: false}
factions.title.color: {description: set player title with color, default: false}
@ -173,6 +179,7 @@ permissions:
factions.documentation.perms: true
factions.documentation.power: true
factions.documentation.ranks: true
factions.documentation.tax: true
factions.documentation.warps: true
factions.expansions: true
factions.faction: true
@ -249,6 +256,11 @@ permissions:
factions.sethome: true
factions.setpower: true
factions.status: true
factions.tax: true
factions.tax.faction: true
factions.tax.player: true
factions.tax.run: true
factions.tax.set: true
factions.name: true
factions.title: true
factions.title.color: true
@ -409,6 +421,11 @@ permissions:
factions.seechunkold: true
factions.sethome: true
factions.status: true
factions.tax: true
factions.tax.faction: true
factions.tax.player: true
factions.tax.run: true
factions.tax.set: true
factions.name: true
factions.title: true
factions.title.color: true

View File

@ -1,7 +1,7 @@
package com.massivecraft.factions.cmd;
import com.massivecraft.factions.cmd.req.ReqTaxEnabled;
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;
@ -10,14 +10,15 @@ import com.massivecraft.massivecore.util.Txt;
import java.util.LinkedHashMap;
public class CmdFactionsTaxInfo extends FactionsCommand
public class CmdFactionsDocumentationTax extends FactionsCommandDocumentation
{
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public CmdFactionsTaxInfo()
public CmdFactionsDocumentationTax()
{
this.addRequirements(ReqTaxEnabled.get());
}
// -------------------------------------------- //
@ -27,23 +28,32 @@ public class CmdFactionsTaxInfo extends FactionsCommand
@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);
msgDoc("<key>Taxation Period: <i>every %s<i>.", periodString);
long nextTaxationTime = MConf.get().taxTaskPeriodMillis + MConf.get().taxTaskPeriodMillis;
msg("<key>Next Taxation: %s", Txt.getTimeDeltaDescriptionRelNow(nextTaxationTime));
msgDoc("<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);
msgDoc("<i>Taxes for players can be set between <reset>%s <i>and <reset>%s<i>.", minTax, maxTax);
double tax = msenderFaction.getTaxForPlayer(msender);
if (tax > 0)
{
msgDoc("<i>You pay <reset>%s <i>in taxes.", Money.format(tax));
}
else if (tax < 0)
{
msgDoc("<i>Instead of taxes you faction pays you <reset>%s <i>.", Money.format(tax * -1));
}
else
{
msgDoc("<i>You don't pay taxes.");
}
}
}

View File

@ -6,6 +6,9 @@ public class CmdFactionsTax extends FactionsCommand
// FIELDS
// -------------------------------------------- //
public CmdFactionsTaxPlayer cmdFactionsTaxPlayer = new CmdFactionsTaxPlayer();
public CmdFactionsTaxFaction cmdFactionsTaxFaction = new CmdFactionsTaxFaction();
public CmdFactionsTaxSet cmdFactionsTaxSet = new CmdFactionsTaxSet();
public CmdFactionsTaxRun cmdFactionsTaxRun = new CmdFactionsTaxRun();
}

View File

@ -0,0 +1,69 @@
package com.massivecraft.factions.cmd;
import com.massivecraft.factions.cmd.req.ReqTaxEnabled;
import com.massivecraft.factions.cmd.type.TypeFaction;
import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MFlag;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.factions.entity.Rank;
import com.massivecraft.massivecore.MassiveException;
import com.massivecraft.massivecore.mson.Mson;
import com.massivecraft.massivecore.util.Txt;
import java.util.Map.Entry;
public class CmdFactionsTaxFaction extends FactionsCommand
{
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public CmdFactionsTaxFaction()
{
this.addParameter(TypeFaction.get(), "faction", "your");
this.addRequirements(ReqTaxEnabled.get());
}
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public void perform() throws MassiveException
{
Faction faction = this.readArg(msenderFaction);
if (faction.isNone())
{
throw new MassiveException().addMsg("<b>Taxes are not in place for %s<b>.", faction.describeTo(msender));
}
Mson title = Txt.titleize("Tax for " + faction.getDisplayName(msender));
message(title);
MFlag flag = MFlag.getFlagTaxKick();
String flagDesc = flag.getStateDesc(faction.getFlag(flag), true, false, false, true, true);
msg("<k>Player Kick: <v>%s", flagDesc);
boolean anyTax = false;
for (Entry<String, Double> entry : faction.getTax().entrySet())
{
String id = entry.getKey();
Double tax = entry.getValue();
Rank rank = faction.getRank(id);
MPlayer mplayer = MPlayer.get(id);
String name;
if (Faction.IDENTIFIER_TAX_BASE.equals(id)) name = "Default";
else if (rank != null) name = rank.getName();
else if (mplayer != null) name = mplayer.getDisplayName(msender);
else continue;
anyTax = true;
msg("<k>%s: <v>%.2f", name, tax);
}
if (!anyTax) msg("<i>No players in this faction pays taxes.");
}
}

View File

@ -0,0 +1,44 @@
package com.massivecraft.factions.cmd;
import com.massivecraft.factions.cmd.req.ReqTaxEnabled;
import com.massivecraft.factions.cmd.type.TypeMPlayer;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.massivecore.MassiveException;
import com.massivecraft.massivecore.money.Money;
import com.massivecraft.massivecore.mson.Mson;
import com.massivecraft.massivecore.util.Txt;
public class CmdFactionsTaxPlayer extends FactionsCommand
{
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public CmdFactionsTaxPlayer()
{
this.addParameter(TypeMPlayer.get(), "player", "you");
this.addRequirements(ReqTaxEnabled.get());
}
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public void perform() throws MassiveException
{
MPlayer mplayer = this.readArg(msender);
Mson title = Txt.titleize("Tax for " + mplayer.getDisplayName(msender));
message(title);
String factionName = mplayer.getFaction().describeTo(msender);
msg("<k>Faction: <v>%s", factionName);
double tax = mplayer.getFaction().getTaxForPlayer(mplayer);
String taxDesc = Money.format(tax);
msg("<k>Tax: <v>%s", taxDesc);
}
}

View File

@ -0,0 +1,28 @@
package com.massivecraft.factions.cmd;
import com.massivecraft.factions.cmd.req.ReqTaxEnabled;
import com.massivecraft.factions.task.TaskTax;
import com.massivecraft.massivecore.MassiveException;
public class CmdFactionsTaxRun extends FactionsCommand
{
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public CmdFactionsTaxRun()
{
this.addRequirements(ReqTaxEnabled.get());
}
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public void perform() throws MassiveException
{
TaskTax.get().invoke(System.currentTimeMillis());
}
}

View File

@ -0,0 +1,90 @@
package com.massivecraft.factions.cmd;
import com.massivecraft.factions.cmd.req.ReqTaxEnabled;
import com.massivecraft.factions.cmd.type.TypeFaction;
import com.massivecraft.factions.cmd.type.TypeTaxable;
import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.entity.MPerm;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.factions.entity.Rank;
import com.massivecraft.massivecore.MassiveException;
import com.massivecraft.massivecore.command.type.primitive.TypeDouble;
import com.massivecraft.massivecore.money.Money;
import com.massivecraft.massivecore.util.MUtil;
public class CmdFactionsTaxSet extends FactionsCommand
{
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public CmdFactionsTaxSet()
{
this.addParameter(TypeDouble.get(), "tax");
this.addParameter(TypeTaxable.get(), "default|rank|player|all", "default");
this.addParameter(TypeFaction.get(), "faction", "your");
this.addRequirements(ReqTaxEnabled.get());
}
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public void perform() throws MassiveException
{
Double tax = this.readArg();
if (MConf.get().taxPlayerMaximum < tax && tax != 0)
{
throw new MassiveException().addMsg("<b>Taxes can't be above <h>%s<b>.", Money.format(MConf.get().taxPlayerMaximum));
}
if (MConf.get().taxPlayerMinimum > tax && tax != 0)
{
throw new MassiveException().addMsg("<b>Taxes can't be below <h>%s<b>.", Money.format(MConf.get().taxPlayerMinimum));
}
Faction faction = this.readArgAt(2, msenderFaction);
TypeTaxable typeTaxable = TypeTaxable.get(faction);
String taxable = this.argIsSet(1) ? typeTaxable.read(this.argAt(1), sender) : Faction.IDENTIFIER_TAX_BASE;
if ( ! MPerm.getPermTax().has(msender, faction, true)) return;
Rank rank = faction.getRank(taxable);
MPlayer mplayer = MPlayer.get(taxable);
String name;
if (Faction.IDENTIFIER_TAX_BASE.equalsIgnoreCase(taxable)) name = "Default";
else if (rank != null) name = rank.getDisplayName(msender);
else if (mplayer != null) name = mplayer.getDisplayName(msender);
else throw new RuntimeException(taxable);
String taxDesc = Money.format(tax);
Double previous = faction.setTaxFor(taxable, tax);
if (MUtil.equalsishNumber(tax, previous))
{
throw new MassiveException().addMsg("<b>The tax for <reset>%s <b>is already <h>%s<b>.", name, taxDesc);
}
msg("<i>The taxes for <reset>%s <i>is now set to <h>%s<i>.", name, taxDesc);
if (tax < 0)
{
msg("<i>NOTE: A negative tax works like a salary.");
}
if (msender != mplayer && mplayer != null)
{
mplayer.msg("%s <i>set your tax to <reset>%s<i>.", msender.getDisplayName(mplayer), taxDesc);
}
if (rank != null)
{
String msg = "<i>Taxes for <reset>%s <i>set to <reset>%s <i>by %s<i>.";
faction.getMPlayersWhereRank(rank).forEach(mp -> mp.msg(msg, name, taxDesc, msender.getDisplayName(mp)));
}
}
}

View File

@ -0,0 +1,40 @@
package com.massivecraft.factions.cmd.req;
import com.massivecraft.factions.task.TaskTax;
import com.massivecraft.massivecore.command.MassiveCommand;
import com.massivecraft.massivecore.command.requirement.RequirementAbstract;
import com.massivecraft.massivecore.util.Txt;
import org.bukkit.command.CommandSender;
public class ReqTaxEnabled extends RequirementAbstract
{
// -------------------------------------------- //
// SERIALIZABLE
// -------------------------------------------- //
private static final long serialVersionUID = 1L;
// -------------------------------------------- //
// INSTANCE & CONSTRUCT
// -------------------------------------------- //
private static ReqTaxEnabled i = new ReqTaxEnabled();
public static ReqTaxEnabled get() { return i; }
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public boolean apply(CommandSender sender, MassiveCommand command)
{
return TaskTax.get().areConditionsMet();
}
@Override
public String createErrorMessage(CommandSender sender, MassiveCommand command)
{
return Txt.parse("<b>Tax is not enabled on this server.");
}
}

View File

@ -0,0 +1,136 @@
package com.massivecraft.factions.cmd.type;
import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.factions.entity.Rank;
import com.massivecraft.massivecore.MassiveException;
import com.massivecraft.massivecore.collections.MassiveList;
import com.massivecraft.massivecore.command.type.TypeAbstract;
import com.massivecraft.massivecore.util.MUtil;
import org.bukkit.command.CommandSender;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
// TODO: This whole thing is a copy/paste of TypeMPermable. Duplicate code.
public class TypeTaxable extends TypeAbstract<String>
{
// -------------------------------------------- //
// INSTANCE & CONSTRUCT
// -------------------------------------------- //
private static TypeTaxable i = new TypeTaxable();
public static TypeTaxable get() { return i; }
private TypeTaxable()
{
super(String.class);
this.faction = null;
}
public static TypeTaxable get(Faction faction) { return new TypeTaxable(faction); }
public TypeTaxable(Faction faction)
{
super(String.class);
if (faction == null) throw new NullPointerException("faction");
this.faction = faction;
}
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
private final Faction faction;
public Faction getFaction() { return this.faction; }
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public String read(String arg, CommandSender sender) throws MassiveException
{
if (arg.toLowerCase().startsWith("rank-"))
{
String subArg = arg.substring("rank-".length());
return new TypeRank(this.getFaction()).read(subArg, sender).getId();
}
if (arg.toLowerCase().startsWith("player-"))
{
String subArg = arg.substring("player-".length());
return TypeMPlayer.get().read(subArg, sender).getId();
}
TypeRank typeRank = new TypeRank(this.getFaction());
try
{
return typeRank.read(arg, sender).getId();
}
catch (MassiveException ex)
{
// Do nothing
}
try
{
return TypeMPlayer.get().read(arg, sender).getId();
}
catch (MassiveException ex)
{
// Do nothing
}
throw new MassiveException().addMsg("<b>No rank or player matches: <h>%s<b>.", arg);
}
public Collection<String> getTabList(CommandSender sender, String arg)
{
List<String> ret = new MassiveList<>();
Faction faction = this.getFaction();
if (faction == null) faction = MPlayer.get(sender).getFaction();
// Always add ranks, relations, other factions and other players
ret.addAll(faction.getRanks().getAll().stream().map(Rank::getName).collect(Collectors.toList()));
ret.addAll(TypeMPlayer.get().getTabList(sender, arg));
// Also add the cases for when type is specified
if (arg.length() >= 2)
{
String compArg = arg.toLowerCase();
if (compArg.startsWith("rank-") || "rank-".startsWith(compArg))
{
ret.addAll(faction.getRanks().getAll().stream()
.map(Rank::getName)
.map(n -> "rank-" + n)
.collect(Collectors.toList()));
}
if (compArg.startsWith("player-") || "player-".startsWith(compArg))
{
ret.addAll(TypeMPlayer.get().getTabList(sender, arg).stream()
.map(s -> "player-" + s)
.collect(Collectors.toList()));
}
}
else
{
// Or at least add the beginning
ret.addAll(MUtil.list("rank-", "player-"));
}
return ret;
}
@Override
public boolean isValid(String arg, CommandSender sender)
{
// In the generic case accept all
if (this.getFaction() == null) return true;
else return super.isValid(arg, sender);
}
}

View File

@ -82,6 +82,7 @@ public class Faction extends Entity<Faction> implements FactionsParticipator, MP
this.setRelationWishes(that.relationWishes);
this.setFlagIds(that.flags);
this.perms = that.perms;
this.tax = that.tax;
return this;
}
@ -165,7 +166,7 @@ public class Faction extends Entity<Faction> implements FactionsParticipator, MP
// What is the base tax on members of the faction?
// Specific taxes on ranks or players.
public static String IDENTIFIER_TAX_BASE = "base";
public static transient String IDENTIFIER_TAX_BASE = "base";
private Map<String, Double> tax = new MassiveMap<>();
@ -1000,6 +1001,7 @@ public class Faction extends Entity<Faction> implements FactionsParticipator, MP
// FINER SET
public Double setTaxFor(String id, Double value)
{
this.changed();
return this.tax.put(id, value);
}

View File

@ -11,8 +11,7 @@ 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.Task;
import com.massivecraft.massivecore.collections.MassiveMap;
import com.massivecraft.massivecore.mixin.MixinMessage;
import com.massivecraft.massivecore.money.Money;
@ -24,18 +23,51 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TaskTax extends Engine
public class TaskTax extends Task
{
// -------------------------------------------- //
// INSTANCE
// -------------------------------------------- //
private static TaskTax i = new TaskTax();
public static TaskTax get() { return i; }
public TaskTax()
{
// Just check once a minute
this.setPeriod(60L * 20L);
this.setPeriod(10L * 20L); // 10 seconds for testing purposes
this.setMustBeTaskServer(true);
this.setLoggingTimeSpent(true);
this.addCondition(Econ::isEnabled);
this.addCondition(() -> MConf.get().taxEnabled);
}
// -------------------------------------------- //
// OVERRIDE: TASK
// -------------------------------------------- //
@Override
public long getPreviousMillis()
{
return MConf.get().taxTaskLastMillis;
}
@Override
public void setPreviousMillis(long millis)
{
MConf.get().taxTaskLastMillis = millis;
MConf.get().changed();
}
@Override
public long getPeriodMillis()
{
return MConf.get().taxTaskPeriodMillis;
}
@Override
public long getOffsetMillis()
{
return MConf.get().taxTaskInvocationOffsetMillis;
}
// -------------------------------------------- //
@ -43,34 +75,6 @@ public class TaskTax extends Engine
// -------------------------------------------- //
@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);
@ -93,7 +97,7 @@ public class TaskTax extends Engine
String debug = taxes.stream()
.map(c -> c.getFirst().getName() + ": " + c.getSecond())
.reduce((s1, s2) -> s1 + "\n" + s2).orElse("");
.reduce((s1, s2) -> s1 + "\n" + s2).orElse("No players pay tax.");
MixinMessage.get().messageAll(debug);
// Pay the highest taxes first.
@ -117,7 +121,7 @@ public class TaskTax extends Engine
// Inform factions
faction2tax.forEach(this::informFactionOfPlayerTax);
// Infrom of taxation complete
// Inform 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);
@ -218,17 +222,4 @@ public class TaskTax extends Engine
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;
}
}