Skip to content

Commit 3938f34

Browse files
authored
Econ Module can work with offlines now (#213)
* Fix: It possible KeyAlreadyExistException workarounded It apparently is possible to have 2 Players with Same Steam ID but different Serial ID. Cause unknown. For both of them the same Identity ID is looked up because that ignores the Serial ID. And therefore I can just replace the value. Could auso check with if(!containsKey) but since the result will be identical I didn't bother. * Fix: Credits are Number-Formatted in all eco commands * Fix: Players can nolonger pay themselves Players were able to duplicate money by paying themselves. If Player A has 10,000 credits And Pays Player A 1,000 credits He ended up with 11,000 credits. But there was a warning on server side that the client was out of sync. And if you do anything else, like for example !econ set !econ take or !econ reset it crashed said client. * Fix: Added missing .DisplayName when Paying player * Feat: !econ check can now also check offline players I dont exactly know why that limitation of online only was in place. set, take, give, check should work for offlines too. Since the others are not important enough (for me at least) I am not doing that with this commit. * Fix: Responses now inform about the possability that the player is offline These kind of commands should work with IMyIdentity instead of IMyPlayer especially since the "*" parameter would work for ALL Players (not NPCs) but since I have no intention to fix it at this point in time. The better message will have to do. * Feat: Econ Module can now also work with offline players * Fix: If more credits are taken than the account has, credits are set to 0. And for transfers proper feedback is provided * Fix: Wrong Sync Client Server fworks correctly now Who would have guessed ChangeBalanceBroadcastClient does NOT broadcast to the client. ChangeBalance does that automatically. The other method is... probably what is called IN the client after receiving the event. * Fix: added forgotten formatting * Fix: More Formatting
1 parent e770de1 commit 3938f34

2 files changed

Lines changed: 161 additions & 113 deletions

File tree

‎Essentials/Commands/EcoModule.cs‎

Lines changed: 155 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,13 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Linq;
43
using System.Text;
5-
using System.Threading.Tasks;
6-
using System.Timers;
7-
using Sandbox;
8-
using Sandbox.Engine.Multiplayer;
94
using Sandbox.Game.GameSystems.BankingAndCurrency;
105
using Sandbox.Game.World;
11-
using Torch;
12-
using Torch.API.Managers;
136
using Torch.Commands;
147
using Torch.Commands.Permissions;
15-
using Torch.Managers.ChatManager;
168
using Torch.Mod;
179
using Torch.Mod.Messages;
1810
using VRage.Game.ModAPI;
19-
using VRageRender.Utils;
2011

2112
namespace Essentials.Commands
2213
{
@@ -25,158 +16,148 @@ public class EcoModule : CommandModule {
2516

2617
[Command("give", "Add a specified anount of credits into a users account. Use '*' to affect all players")]
2718
[Permission(MyPromoteLevel.Admin)]
28-
public void EcoGive(string Player, long amount) {
29-
if (Player != "*") {
30-
var p = Utilities.GetPlayerByNameOrId(Player);
31-
if (p == null) {
32-
Context.Respond("Player is not online or cannot be found!");
33-
return;
34-
}
35-
p.TryGetBalanceInfo(out long balance);
36-
Context.Respond($"new bal will be {balance + amount:#,##0}");
37-
p.RequestChangeBalance(amount);
38-
ModCommunication.SendMessageTo(new NotificationMessage($"{amount:#,##0} credits have been added to your virtual account", 10000, "Blue"), p.SteamUserId);
19+
public void EcoGive(string player, long amount, bool onlyOnline = false, bool excludeNpcs = true) {
3920

21+
if(!TryFindPlayerIdentities(player, onlyOnline, excludeNpcs, out List<long> foundIdentities)) {
22+
Context.Respond("Player cannot be found!");
23+
return;
4024
}
41-
else {
42-
foreach (var p in MySession.Static.Players.GetAllPlayers()) {
43-
long IdentityID = MySession.Static.Players.TryGetIdentityId(p.SteamId);
44-
MyBankingSystem.ChangeBalance(IdentityID, amount);
45-
ModCommunication.SendMessageTo(new NotificationMessage($"{amount:#,##0} credits have been added to your virtual account", 10000, "Blue"), p.SteamId);
46-
}
25+
26+
int changedIdentities = 0;
27+
28+
foreach (long identityId in foundIdentities) {
29+
30+
ChangeBalance(identityId, amount);
31+
32+
ulong steamId = Utilities.GetSteamId(identityId);
33+
34+
ModCommunication.SendMessageTo(new NotificationMessage($"{amount:#,##0} credits have been added to your virtual account", 10000, "Blue"), steamId);
35+
36+
changedIdentities++;
4737
}
48-
Context.Respond($"{amount:#,##0} credits given to account(s)");
38+
39+
Context.Respond($"{amount:#,##0} credits given to {changedIdentities:#,##0} account(s)");
4940
}
5041

5142
[Command("take", "Take a specified anount of credits from a users account. Use '*' to affect all players")]
5243
[Permission(MyPromoteLevel.Admin)]
53-
public void EcoTake(string Player, long amount) {
54-
if (Player != "*") {
55-
var p = Utilities.GetPlayerByNameOrId(Player);
56-
if (p == null) {
57-
Context.Respond("Player is not online or cannot be found!");
58-
return;
59-
}
60-
long changefactor = 0 - amount;
61-
p.RequestChangeBalance(changefactor);
62-
ModCommunication.SendMessageTo(new NotificationMessage($"{amount:#,##0} credits have been taken to your virtual account", 10000, "Blue"), p.SteamUserId);
44+
public void EcoTake(string player, long amount, bool onlyOnline = false, bool excludeNpcs = true) {
45+
46+
if (!TryFindPlayerIdentities(player, onlyOnline, excludeNpcs, out List<long> foundIdentities)) {
47+
Context.Respond("Player cannot be found!");
48+
return;
6349
}
64-
else {
65-
foreach (var p in MySession.Static.Players.GetAllPlayers()) {
66-
long IdentityID = MySession.Static.Players.TryGetIdentityId(p.SteamId);
67-
long balance = MyBankingSystem.GetBalance(IdentityID);
68-
MyBankingSystem.ChangeBalance(IdentityID, -amount);
69-
ModCommunication.SendMessageTo(new NotificationMessage($"{amount:#,##0} credits have been taken to your virtual account", 10000, "Blue"), p.SteamId);
70-
}
50+
51+
int changedIdentities = 0;
52+
53+
foreach (long identityId in foundIdentities) {
54+
55+
ChangeBalance(identityId, -amount);
56+
57+
ulong steamId = Utilities.GetSteamId(identityId);
58+
59+
ModCommunication.SendMessageTo(new NotificationMessage($"{amount:#,##0} credits have been taken from your virtual account", 10000, "Blue"), steamId);
60+
61+
changedIdentities++;
7162
}
72-
Context.Respond($"{amount:#,##0} credits taken from account(s)");
63+
64+
Context.Respond($"{amount:#,##0} credits taken from {changedIdentities:#,##0} account(s)");
7365
}
7466

7567
[Command("set", "Set a users account to a specifed balance. Use '*' to affect all players")]
7668
[Permission(MyPromoteLevel.Admin)]
77-
public void EcoSet(string Player, long amount) {
78-
if (Player != "*") {
79-
var p = Utilities.GetPlayerByNameOrId(Player);
80-
if (p == null) {
81-
Context.Respond("Player is not online or cannot be found!");
82-
return;
83-
}
84-
p.TryGetBalanceInfo(out long balance);
85-
long difference = (balance - amount);
86-
p.RequestChangeBalance(-difference);
87-
ModCommunication.SendMessageTo(new NotificationMessage($"Your balance has been set to {amount:#,##0} credits!", 10000, "Blue"), p.SteamUserId);
69+
public void EcoSet(string player, long amount, bool onlyOnline = false, bool excludeNpcs = true) {
70+
71+
if (!TryFindPlayerIdentities(player, onlyOnline, excludeNpcs, out List<long> foundIdentities)) {
72+
Context.Respond("Player cannot be found!");
73+
return;
8874
}
89-
else {
90-
foreach (var p in MySession.Static.Players.GetAllPlayers()) {
91-
long IdentityID = MySession.Static.Players.TryGetIdentityId(p.SteamId);
92-
long balance = MyBankingSystem.GetBalance(IdentityID);
93-
long difference = (balance - amount);
94-
MyBankingSystem.ChangeBalance(IdentityID, -difference);
95-
ModCommunication.SendMessageTo(new NotificationMessage($"Your balance has been set to {amount:#,##0} credits!", 10000, "Blue"), p.SteamId);
96-
}
75+
76+
int changedIdentities = 0;
77+
78+
foreach (long identityId in foundIdentities) {
79+
80+
long balance = MyBankingSystem.GetBalance(identityId);
81+
82+
ChangeBalance(identityId, -(balance - amount));
83+
84+
ulong steamId = Utilities.GetSteamId(identityId);
85+
86+
ModCommunication.SendMessageTo(new NotificationMessage($"Your balance has been set to {amount:#,##0} credits!", 10000, "Blue"), steamId);
87+
88+
changedIdentities++;
9789
}
98-
Context.Respond($"Balance(s) set to {amount:#,##0}");
90+
91+
Context.Respond($"Balance(s) set to {amount:#,##0} on {changedIdentities:#,##0} accounts");
9992
}
10093

10194
[Command("reset", "Reset the credits in a users account to 10,000. Use '*' to affect all players")]
10295
[Permission(MyPromoteLevel.Admin)]
103-
public void EcoReset(string Player) {
104-
if (Player != "*") {
105-
var p = Utilities.GetPlayerByNameOrId(Player);
106-
if (p == null) {
107-
Context.Respond("Player is not online or cannot be found!");
108-
return;
109-
}
110-
p.TryGetBalanceInfo(out long balance);
111-
long difference = (balance - 10000);
112-
p.RequestChangeBalance(-difference);
113-
ModCommunication.SendMessageTo(new NotificationMessage($"Your balance has been reset to 10,000 credits!", 10000, "Blue"), p.SteamUserId);
114-
}
115-
else {
116-
foreach (var p in MySession.Static.Players.GetAllPlayers()) {
117-
long IdentityID = MySession.Static.Players.TryGetIdentityId(p.SteamId);
118-
long balance = MyBankingSystem.GetBalance(IdentityID);
119-
long difference = (balance - 10000);
120-
MyBankingSystem.ChangeBalance(IdentityID, -difference);
121-
ModCommunication.SendMessageTo(new NotificationMessage($"Your balance has been reset to 10,000 credits!", 10000, "Blue"), p.SteamId);
122-
}
123-
}
124-
Context.Respond("Balance(s) reset to 10,000 credits");
96+
public void EcoReset(string player, bool onlyOnline = false, bool excludeNpcs = true) {
97+
EcoSet(player, 10_000, onlyOnline, excludeNpcs);
12598
}
12699

127100
[Command("top", "Return a list of each players balance on the server sorted from highest to lowest")]
128101
[Permission(MyPromoteLevel.None)]
129-
public void EcoTop() {
102+
public void EcoTop(bool onlyOnline = false, bool excludeNpcs = true) {
103+
104+
TryFindPlayerIdentities("*", onlyOnline, excludeNpcs, out List<long> foundIdentities);
105+
106+
var players = MySession.Static.Players;
107+
108+
Dictionary<MyIdentity, long> balances = new Dictionary<MyIdentity, long>();
109+
foreach (long identityId in foundIdentities) {
110+
111+
var identity = players.TryGetIdentity(identityId);
112+
long balance = MyBankingSystem.GetBalance(identityId);
113+
114+
balances[identity] = balance;
115+
}
116+
130117
StringBuilder ecodata = new StringBuilder();
131118
ecodata.AppendLine("Summary of balanaces accross the server");
132-
Dictionary<ulong, long> balances = new Dictionary<ulong, long>();
133-
foreach (var p in MySession.Static.Players.GetAllPlayers()) {
134-
135-
long IdentityID = MySession.Static.Players.TryGetIdentityId(p.SteamId);
136-
long balance = MyBankingSystem.GetBalance(IdentityID);
137-
138-
/*
139-
* Add or Update. We have seen that it is possible to have
140-
* two players with the same SteamID but different SerialIDs.
141-
*
142-
* Those also had different identities. But one of which was dead.
143-
* TryGetIdentityId() Returned the same value in both cases. So no damage done if
144-
* Value is just overwritten.
145-
*/
146-
balances[p.SteamId] = balance;
147-
}
148-
var sorted = balances.OrderByDescending(x => x.Value).ThenBy(x => x.Key);
119+
120+
var sorted = balances.OrderByDescending(x => x.Value).ThenBy(x => x.Key.DisplayName);
149121
foreach (var value in sorted) {
150-
var test = MySession.Static.Players.TryGetIdentityNameFromSteamId(value.Key);
151-
ecodata.AppendLine($"Player: {MySession.Static.Players.TryGetIdentityNameFromSteamId(value.Key)} - Balance: {value.Value:#,##0}");
122+
123+
var identity = value.Key;
124+
125+
ecodata.AppendLine($"Player: {identity.DisplayName} - Balance: {value.Value:#,##0}");
152126
}
153127

154128
if (Context.Player == null) {
155129
Context.Respond(ecodata.ToString());
156130
return;
157131
}
132+
158133
ModCommunication.SendMessageTo(new DialogMessage("Public balance list", "List of players and their credit balances", ecodata.ToString()), Context.Player.SteamUserId);
159134
}
160135

161136
[Command("check", "Check the balance of a specific player")]
162137
[Permission(MyPromoteLevel.None)]
163138
public void EcoCheck(string Player) {
139+
164140
var p = Utilities.GetIdentityByNameOrIds(Player);
165141
if (p == null) {
166142
Context.Respond("Player cannot be found!");
167143
return;
168144
}
145+
169146
long balance = MyBankingSystem.GetBalance(p.IdentityId);
147+
170148
Context.Respond($"{p.DisplayName}'s balance is {balance:#,##0} credits");
171149
}
172150

173151
[Command("pay")]
174152
[Permission(MyPromoteLevel.None)]
175153
public void EcoPay(string Player, long amount) {
154+
176155
if (Context.Player == null) {
177156
Context.Respond("Console cannot execute this command");
178157
return;
179158
}
159+
160+
/* We are purposely keeping the online check in this method. Otherwise it could cause confusion with players. */
180161
var p = Utilities.GetPlayerByNameOrId(Player);
181162
if (p == null) {
182163
Context.Respond("Player is not online or cannot be found!");
@@ -186,18 +167,80 @@ public void EcoPay(string Player, long amount) {
186167
var fromIdentitiyId = Context.Player.Identity.IdentityId;
187168
var toIdentitiyId = p.Identity.IdentityId;
188169

189-
if(fromIdentitiyId == toIdentitiyId) {
170+
if (fromIdentitiyId == toIdentitiyId) {
190171
Context.Respond("You cannot pay yourself!");
191172
return;
192173
}
193174

194175
var finalFromBalance = MyBankingSystem.GetBalance(fromIdentitiyId) - amount;
195176
var finalToBalance = MyBankingSystem.GetBalance(toIdentitiyId) + amount;
196-
177+
178+
if(finalFromBalance < 0) {
179+
Context.Respond($"Sorry, but you are short {-finalFromBalance:#,##0} credits!");
180+
return;
181+
}
182+
197183
MyBankingSystem.RequestTransfer_BroadcastToClients(Context.Player.Identity.IdentityId, p.Identity.IdentityId, amount, finalFromBalance, finalToBalance);
198-
ModCommunication.SendMessageTo(new NotificationMessage($"Your have recieved {amount:#,##0} credits from {Context.Player.DisplayName}!", 10000, "Blue"),p.SteamUserId);
199-
ModCommunication.SendMessageTo(new NotificationMessage($"Your have sent {amount:#,##0} credits to {p.DisplayName}!", 10000, "Blue"),Context.Player.SteamUserId);
184+
ModCommunication.SendMessageTo(new NotificationMessage($"Your have recieved {amount:#,##0} credits from {Context.Player.DisplayName}!", 10000, "Blue"), p.SteamUserId);
185+
ModCommunication.SendMessageTo(new NotificationMessage($"Your have sent {amount:#,##0} credits to {p.DisplayName}!", 10000, "Blue"), Context.Player.SteamUserId);
200186
}
201187

188+
/// <summary>
189+
/// This method changes the balance of the given identity by the passed amount.
190+
/// If the amount is positive the player receives credits. If it is negative, the player loses credits.
191+
///
192+
/// If the amount taken from a users account is greater than the amount the user has, the accounts balance is set to 0, since negative balances are not possible.
193+
/// </summary>
194+
private void ChangeBalance(long identityId, long amount) {
195+
196+
long balance = MyBankingSystem.GetBalance(identityId);
197+
198+
if (balance + amount < 0)
199+
amount = -balance;
200+
201+
MyBankingSystem.ChangeBalance(identityId, amount);
202+
}
203+
204+
/// <summary>
205+
/// This method take all identities of the server and assembles a list with the respective identityIDs.
206+
/// It is possible to filter this list to not contain NPCs or only contain players that are currently online.
207+
///
208+
/// Returns true if the given playerName exists and could be found, or the playerName is "*" false otherwise.
209+
/// Even if true is returned the list of foundIdentities can be empty if the online or npc filters got applied.
210+
/// </summary>
211+
private bool TryFindPlayerIdentities(string playerName, bool onlyOnline, bool excludeNpcs, out List<long> foundIdentities) {
212+
213+
var relevantIdentities = new List<long>();
214+
var players = MySession.Static.Players;
215+
216+
if (playerName != "*") {
217+
218+
var identity = Utilities.GetIdentityByNameOrIds(playerName);
219+
220+
if (identity == null) {
221+
foundIdentities = relevantIdentities;
222+
return false;
223+
}
224+
225+
relevantIdentities.Add(identity.IdentityId);
226+
227+
} else {
228+
229+
relevantIdentities.AddRange(players.GetAllIdentities()
230+
.Select(identity => identity.IdentityId));
231+
}
232+
233+
IEnumerable<long> identitiesToCheck = relevantIdentities;
234+
235+
if (onlyOnline)
236+
identitiesToCheck = identitiesToCheck.Where(identityId => players.IsPlayerOnline(identityId));
237+
238+
if (excludeNpcs)
239+
identitiesToCheck = identitiesToCheck.Where(identityId => !players.IdentityIsNpc(identityId));
240+
241+
foundIdentities = identitiesToCheck.ToList();
242+
243+
return true;
244+
}
202245
}
203246
}

‎Essentials/Utilities.cs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public static IMyIdentity GetIdentityByNameOrIds(string playerNameOrIds)
119119

120120
if (ulong.TryParse(playerNameOrIds, out ulong steamId))
121121
{
122-
ulong id = MySession.Static.Players.TryGetSteamId(identity.IdentityId);
122+
ulong id = GetSteamId(identity.IdentityId);
123123
if (id == steamId)
124124
return identity;
125125
}
@@ -152,6 +152,11 @@ public static IMyPlayer GetPlayerByNameOrId(string nameOrPlayerId)
152152
return null;
153153
}
154154

155+
public static ulong GetSteamId(long identityId)
156+
{
157+
return MySession.Static.Players.TryGetSteamId(identityId);
158+
}
159+
155160
public static int GetOnlinePlayerCount()
156161
{
157162
var result = 0;

0 commit comments

Comments
 (0)