Merge branch 'general-devel' into add-moondial-permission
This commit is contained in:
commit
c89c34b356
36 changed files with 713 additions and 236 deletions
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
|
@ -1,2 +1,2 @@
|
||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
github: [SignatureBeef, hakusaro, Stealownz, QuiCM]
|
github: [SignatureBeef, QuiCM]
|
||||||
|
|
|
||||||
39
.github/workflows/ci-docker.yml
vendored
39
.github/workflows/ci-docker.yml
vendored
|
|
@ -5,6 +5,10 @@ on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
@ -14,12 +18,43 @@ jobs:
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up buildx
|
- name: Set up buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Login to ghcr.io
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Generate version information
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ghcr.io/${{ github.repository }}
|
||||||
|
tags: |
|
||||||
|
type=schedule
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=tag,enable=${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
type=semver,pattern={{major}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
flavor: |
|
||||||
|
latest=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
- name: Build image
|
- name: Build image
|
||||||
uses: docker/build-push-action@v5
|
id: build
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,windows/amd64
|
platforms: linux/amd64,linux/arm64,linux/arm/v7,windows/amd64
|
||||||
push: false
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
pull: true
|
pull: true
|
||||||
cache-from: type=gha, scope=${{ github.workflow }}
|
cache-from: type=gha, scope=${{ github.workflow }}
|
||||||
cache-to: type=gha, scope=${{ github.workflow }}
|
cache-to: type=gha, scope=${{ github.workflow }}
|
||||||
|
- name: Generate build provenance attestation
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
uses: actions/attest-build-provenance@v2
|
||||||
|
with:
|
||||||
|
subject-name: ghcr.io/${{ github.repository }}
|
||||||
|
subject-digest: ${{ steps.build.outputs.digest }}
|
||||||
|
push-to-registry: true
|
||||||
|
|
|
||||||
4
.github/workflows/ci-otapi3.yml
vendored
4
.github/workflows/ci-otapi3.yml
vendored
|
|
@ -63,14 +63,14 @@ jobs:
|
||||||
tar -cvf ../../../../../../TShock-Beta-${{ matrix.arch }}-Release.tar *
|
tar -cvf ../../../../../../TShock-Beta-${{ matrix.arch }}-Release.tar *
|
||||||
|
|
||||||
- name: Upload artifact (non-Windows)
|
- name: Upload artifact (non-Windows)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ matrix.arch != 'win-x64' }}
|
if: ${{ matrix.arch != 'win-x64' }}
|
||||||
with:
|
with:
|
||||||
name: TShock-Beta-${{ matrix.arch }}-Release
|
name: TShock-Beta-${{ matrix.arch }}-Release
|
||||||
path: TShock-Beta-${{ matrix.arch }}-Release.tar
|
path: TShock-Beta-${{ matrix.arch }}-Release.tar
|
||||||
|
|
||||||
- name: Upload artifact (Windows)
|
- name: Upload artifact (Windows)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ matrix.arch == 'win-x64' }}
|
if: ${{ matrix.arch == 'win-x64' }}
|
||||||
with:
|
with:
|
||||||
name: TShock-Beta-${{ matrix.arch }}-Release
|
name: TShock-Beta-${{ matrix.arch }}-Release
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://tshock.co/newlogo.png" alt="TShock for Terraria"><br />
|
<img src="https://tshock.s3.us-west-001.backblazeb2.com/newlogo.png" alt="TShock for Terraria"><br />
|
||||||
<a href="https://ci.appveyor.com/project/hakusaro/tshock">
|
<a href="https://ci.appveyor.com/project/hakusaro/tshock">
|
||||||
<img src="https://ci.appveyor.com/api/projects/status/chhe61q227lqdlg1?svg=true" alt="AppVeyor Build Status">
|
<img src="https://ci.appveyor.com/api/projects/status/chhe61q227lqdlg1?svg=true" alt="AppVeyor Build Status">
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://tshock.co/newlogo.png" alt="TShock for Terraria"><br />
|
<img src="https://tshock.s3.us-west-001.backblazeb2.com/newlogo.png" alt="TShock for Terraria"><br />
|
||||||
<a href="https://ci.appveyor.com/project/hakusaro/tshock">
|
<a href="https://ci.appveyor.com/project/hakusaro/tshock">
|
||||||
<img src="https://ci.appveyor.com/api/projects/status/chhe61q227lqdlg1?svg=true" alt="AppVeyor Build Status">
|
<img src="https://ci.appveyor.com/api/projects/status/chhe61q227lqdlg1?svg=true" alt="AppVeyor Build Status">
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -424,7 +424,7 @@ namespace TShockAPI
|
||||||
};
|
};
|
||||||
PlayerAddBuffWhitelist[BuffID.BrainOfConfusionBuff] = new BuffLimit
|
PlayerAddBuffWhitelist[BuffID.BrainOfConfusionBuff] = new BuffLimit
|
||||||
{
|
{
|
||||||
MaxTicks = 240,
|
MaxTicks = 60 * 4,
|
||||||
CanBeAddedWithoutHostile = true,
|
CanBeAddedWithoutHostile = true,
|
||||||
CanOnlyBeAppliedToSender = true
|
CanOnlyBeAppliedToSender = true
|
||||||
};
|
};
|
||||||
|
|
@ -434,6 +434,12 @@ namespace TShockAPI
|
||||||
CanBeAddedWithoutHostile = true,
|
CanBeAddedWithoutHostile = true,
|
||||||
CanOnlyBeAppliedToSender = true
|
CanOnlyBeAppliedToSender = true
|
||||||
};
|
};
|
||||||
|
PlayerAddBuffWhitelist[BuffID.ParryDamageBuff] = new BuffLimit
|
||||||
|
{
|
||||||
|
MaxTicks = 60 * 5,
|
||||||
|
CanBeAddedWithoutHostile = true,
|
||||||
|
CanOnlyBeAppliedToSender = true
|
||||||
|
};
|
||||||
|
|
||||||
#endregion Whitelist
|
#endregion Whitelist
|
||||||
}
|
}
|
||||||
|
|
@ -498,6 +504,14 @@ namespace TShockAPI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y))
|
||||||
|
{
|
||||||
|
TShock.Log.ConsoleInfo(GetString("Bouncer / OnPlayerUpdate force kicked (attempted to set position to infinity or NaN) from {0}", args.Player.Name));
|
||||||
|
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (pos.X < 0 || pos.Y < 0 || pos.X >= Main.maxTilesX * 16 - 16 || pos.Y >= Main.maxTilesY * 16 - 16)
|
if (pos.X < 0 || pos.Y < 0 || pos.X >= Main.maxTilesX * 16 - 16 || pos.Y >= Main.maxTilesY * 16 - 16)
|
||||||
{
|
{
|
||||||
TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlayerUpdate rejected from (position check) {0}", args.Player.Name));
|
TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlayerUpdate rejected from (position check) {0}", args.Player.Name));
|
||||||
|
|
@ -1066,6 +1080,22 @@ namespace TShockAPI
|
||||||
bool noDelay = args.NoDelay;
|
bool noDelay = args.NoDelay;
|
||||||
short type = args.Type;
|
short type = args.Type;
|
||||||
|
|
||||||
|
if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y))
|
||||||
|
{
|
||||||
|
TShock.Log.ConsoleInfo(GetString("Bouncer / OnItemDrop force kicked (attempted to set position to infinity or NaN) from {0}", args.Player.Name));
|
||||||
|
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!float.IsFinite(vel.X) || !float.IsFinite(vel.Y))
|
||||||
|
{
|
||||||
|
TShock.Log.ConsoleInfo(GetString("Bouncer / OnItemDrop force kicked (attempted to set velocity to infinity or NaN) from {0}", args.Player.Name));
|
||||||
|
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// player is attempting to crash clients
|
// player is attempting to crash clients
|
||||||
if (type < -48 || type >= Terraria.ID.ItemID.Count)
|
if (type < -48 || type >= Terraria.ID.ItemID.Count)
|
||||||
{
|
{
|
||||||
|
|
@ -1169,6 +1199,22 @@ namespace TShockAPI
|
||||||
int index = args.Index;
|
int index = args.Index;
|
||||||
float[] ai = args.Ai;
|
float[] ai = args.Ai;
|
||||||
|
|
||||||
|
if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y))
|
||||||
|
{
|
||||||
|
TShock.Log.ConsoleInfo(GetString("Bouncer / OnNewProjectile force kicked (attempted to set position to infinity or NaN) from {0}", args.Player.Name));
|
||||||
|
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!float.IsFinite(vel.X) || !float.IsFinite(vel.Y))
|
||||||
|
{
|
||||||
|
TShock.Log.ConsoleInfo(GetString("Bouncer / OnNewProjectile force kicked (attempted to set velocity to infinity or NaN) from {0}", args.Player.Name));
|
||||||
|
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (index > Main.maxProjectiles)
|
if (index > Main.maxProjectiles)
|
||||||
{
|
{
|
||||||
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from above projectile limit from {0}", args.Player.Name));
|
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from above projectile limit from {0}", args.Player.Name));
|
||||||
|
|
@ -1878,7 +1924,7 @@ namespace TShockAPI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TShock.Players[id] == null)
|
if (TShock.Players[id] == null || !TShock.Players[id].Active)
|
||||||
{
|
{
|
||||||
TShock.Log.ConsoleDebug(GetString(
|
TShock.Log.ConsoleDebug(GetString(
|
||||||
"Bouncer / OnPlayerBuff rejected {0} ({1}) applying buff {2} to {3} for {4} ticks: target is null", args.Player.Name,
|
"Bouncer / OnPlayerBuff rejected {0} ({1}) applying buff {2} to {3} for {4} ticks: target is null", args.Player.Name,
|
||||||
|
|
@ -2081,7 +2127,7 @@ namespace TShockAPI
|
||||||
short amount = args.Amount;
|
short amount = args.Amount;
|
||||||
byte plr = args.TargetPlayerIndex;
|
byte plr = args.TargetPlayerIndex;
|
||||||
|
|
||||||
if (amount <= 0 || Main.player[plr] == null || !Main.player[plr].active)
|
if (amount <= 0 || TShock.Players[plr] == null || !TShock.Players[plr].Active)
|
||||||
{
|
{
|
||||||
TShock.Log.ConsoleDebug(GetString("Bouncer / OnHealOtherPlayer rejected null checks"));
|
TShock.Log.ConsoleDebug(GetString("Bouncer / OnHealOtherPlayer rejected null checks"));
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|
@ -2589,7 +2635,7 @@ namespace TShockAPI
|
||||||
byte direction = args.Direction;
|
byte direction = args.Direction;
|
||||||
PlayerDeathReason reason = args.PlayerDeathReason;
|
PlayerDeathReason reason = args.PlayerDeathReason;
|
||||||
|
|
||||||
if (id >= Main.maxPlayers || TShock.Players[id] == null)
|
if (id >= Main.maxPlayers || TShock.Players[id] == null || !TShock.Players[id].Active)
|
||||||
{
|
{
|
||||||
TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlayerDamage rejected null check"));
|
TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlayerDamage rejected null check"));
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|
@ -2854,7 +2900,7 @@ namespace TShockAPI
|
||||||
{ BuffID.Poisoned, 3600 }, // BuffID: 20
|
{ BuffID.Poisoned, 3600 }, // BuffID: 20
|
||||||
{ BuffID.OnFire, 1200 }, // BuffID: 24
|
{ BuffID.OnFire, 1200 }, // BuffID: 24
|
||||||
{ BuffID.Confused, short.MaxValue }, // BuffID: 31 Brain of Confusion Internal Item ID: 3223
|
{ BuffID.Confused, short.MaxValue }, // BuffID: 31 Brain of Confusion Internal Item ID: 3223
|
||||||
{ BuffID.CursedInferno, 420 }, // BuffID: 39
|
{ BuffID.CursedInferno, 600 }, // BuffID: 39
|
||||||
{ BuffID.Frostburn, 900 }, // BuffID: 44
|
{ BuffID.Frostburn, 900 }, // BuffID: 44
|
||||||
{ BuffID.Ichor, 1200 }, // BuffID: 69
|
{ BuffID.Ichor, 1200 }, // BuffID: 69
|
||||||
{ BuffID.Venom, 1800 }, // BuffID: 70
|
{ BuffID.Venom, 1800 }, // BuffID: 70
|
||||||
|
|
|
||||||
|
|
@ -148,24 +148,29 @@ namespace TShockAPI
|
||||||
Permissions = new List<string>();
|
Permissions = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Run(string msg, bool silent, TSPlayer ply, List<string> parms)
|
public bool Run(CommandArgs args)
|
||||||
{
|
{
|
||||||
if (!CanRun(ply))
|
if (!CanRun(args.Player))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CommandDelegate(new CommandArgs(msg, silent, ply, parms));
|
CommandDelegate(args);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
ply.SendErrorMessage(GetString("Command failed, check logs for more details."));
|
args.Player.SendErrorMessage(GetString("Command failed, check logs for more details."));
|
||||||
TShock.Log.Error(e.ToString());
|
TShock.Log.Error(e.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Run(string msg, bool silent, TSPlayer ply, List<string> parms)
|
||||||
|
{
|
||||||
|
return Run(new CommandArgs(msg, silent, ply, parms));
|
||||||
|
}
|
||||||
|
|
||||||
public bool Run(string msg, TSPlayer ply, List<string> parms)
|
public bool Run(string msg, TSPlayer ply, List<string> parms)
|
||||||
{
|
{
|
||||||
return Run(msg, false, ply, parms);
|
return Run(msg, false, ply, parms);
|
||||||
|
|
@ -704,7 +709,12 @@ namespace TShockAPI
|
||||||
TShock.Utils.SendLogs(GetString("{0} executed: {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdText), Color.PaleVioletRed, player);
|
TShock.Utils.SendLogs(GetString("{0} executed: {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdText), Color.PaleVioletRed, player);
|
||||||
else
|
else
|
||||||
TShock.Utils.SendLogs(GetString("{0} executed (args omitted): {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdName), Color.PaleVioletRed, player);
|
TShock.Utils.SendLogs(GetString("{0} executed (args omitted): {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdName), Color.PaleVioletRed, player);
|
||||||
cmd.Run(cmdText, silent, player, args);
|
|
||||||
|
CommandArgs arguments = new CommandArgs(cmdText, silent, player, args);
|
||||||
|
bool handled = PlayerHooks.OnPrePlayerCommand(cmd, ref arguments);
|
||||||
|
if (!handled)
|
||||||
|
cmd.Run(arguments);
|
||||||
|
PlayerHooks.OnPostPlayerCommand(cmd, arguments, handled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1176,7 +1186,7 @@ namespace TShockAPI
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TShock.UserAccounts.SetUserGroup(account, args.Parameters[2]);
|
TShock.UserAccounts.SetUserGroup(args.Player, account, args.Parameters[2]);
|
||||||
TShock.Log.ConsoleInfo(GetString("{0} changed account {1} to group {2}.", args.Player.Name, account.Name, args.Parameters[2]));
|
TShock.Log.ConsoleInfo(GetString("{0} changed account {1} to group {2}.", args.Player.Name, account.Name, args.Parameters[2]));
|
||||||
args.Player.SendSuccessMessage(GetString("Account {0} has been changed to group {1}.", account.Name, args.Parameters[2]));
|
args.Player.SendSuccessMessage(GetString("Account {0} has been changed to group {1}.", account.Name, args.Parameters[2]));
|
||||||
|
|
||||||
|
|
@ -1193,6 +1203,10 @@ namespace TShockAPI
|
||||||
{
|
{
|
||||||
args.Player.SendErrorMessage(GetString($"User {account.Name} does not exist."));
|
args.Player.SendErrorMessage(GetString($"User {account.Name} does not exist."));
|
||||||
}
|
}
|
||||||
|
catch (UserGroupUpdateLockedException)
|
||||||
|
{
|
||||||
|
args.Player.SendErrorMessage(GetString("Hook blocked the attempt to change the user group."));
|
||||||
|
}
|
||||||
catch (UserAccountManagerException e)
|
catch (UserAccountManagerException e)
|
||||||
{
|
{
|
||||||
args.Player.SendErrorMessage(GetString($"User {account.Name} could not be added. Check console for details."));
|
args.Player.SendErrorMessage(GetString($"User {account.Name} could not be added. Check console for details."));
|
||||||
|
|
@ -2044,6 +2058,7 @@ namespace TShockAPI
|
||||||
private static void OffNoSave(CommandArgs args)
|
private static void OffNoSave(CommandArgs args)
|
||||||
{
|
{
|
||||||
string reason = ((args.Parameters.Count > 0) ? GetString("Server shutting down: ") + String.Join(" ", args.Parameters) : GetString("Server shutting down."));
|
string reason = ((args.Parameters.Count > 0) ? GetString("Server shutting down: ") + String.Join(" ", args.Parameters) : GetString("Server shutting down."));
|
||||||
|
Netplay.SaveOnServerExit = false;
|
||||||
TShock.Utils.StopServer(false, reason);
|
TShock.Utils.StopServer(false, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3070,12 +3085,12 @@ namespace TShockAPI
|
||||||
args.Player.SendErrorMessage(GetString("You do not have permission to teleport all other players."));
|
args.Player.SendErrorMessage(GetString("You do not have permission to teleport all other players."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < Main.maxPlayers; i++)
|
foreach (var player in TShock.Players)
|
||||||
{
|
{
|
||||||
if (Main.player[i].active && (Main.player[i] != args.TPlayer))
|
if (player != null && player.Active && player.Index != args.Player.Index)
|
||||||
{
|
{
|
||||||
if (TShock.Players[i].Teleport(args.TPlayer.position.X, args.TPlayer.position.Y))
|
if (player.Teleport(args.TPlayer.position.X, args.TPlayer.position.Y))
|
||||||
TShock.Players[i].SendSuccessMessage(GetString("You were teleported to {0}.", args.Player.Name));
|
player.SendSuccessMessage(GetString("You were teleported to {0}.", args.Player.Name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args.Player.SendSuccessMessage(GetString("Teleported everyone to yourself."));
|
args.Player.SendSuccessMessage(GetString("Teleported everyone to yourself."));
|
||||||
|
|
@ -4622,21 +4637,22 @@ namespace TShockAPI
|
||||||
{
|
{
|
||||||
if (args.Parameters.Count != 1)
|
if (args.Parameters.Count != 1)
|
||||||
{
|
{
|
||||||
args.Player.SendErrorMessage(GetString("Invalid syntax. Proper syntax: {0}wind <speed>.", Specifier));
|
args.Player.SendErrorMessage(GetString("Invalid syntax. Proper syntax: {0}wind <speed in mph>.", Specifier));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int speed;
|
float mph;
|
||||||
if (!int.TryParse(args.Parameters[0], out speed) || speed * 100 < 0)
|
if (!float.TryParse(args.Parameters[0], out mph) || mph is < -40f or > 40f)
|
||||||
{
|
{
|
||||||
args.Player.SendErrorMessage(GetString("Invalid wind speed."));
|
args.Player.SendErrorMessage(GetString("Invalid wind speed (must be between -40 and 40)."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float speed = mph / 50f; // -40 to 40 mph -> -0.8 to 0.8
|
||||||
Main.windSpeedCurrent = speed;
|
Main.windSpeedCurrent = speed;
|
||||||
Main.windSpeedTarget = speed;
|
Main.windSpeedTarget = speed;
|
||||||
TSPlayer.All.SendData(PacketTypes.WorldInfo);
|
TSPlayer.All.SendData(PacketTypes.WorldInfo);
|
||||||
TSPlayer.All.SendInfoMessage(GetString("{0} changed the wind speed to {1}.", args.Player.Name, speed));
|
TSPlayer.All.SendInfoMessage(GetString("{0} changed the wind speed to {1}mph.", args.Player.Name, mph));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Time/PvpFun Commands
|
#endregion Time/PvpFun Commands
|
||||||
|
|
@ -5347,7 +5363,7 @@ namespace TShockAPI
|
||||||
|
|
||||||
foreach (TSPlayer ply in TShock.Players)
|
foreach (TSPlayer ply in TShock.Players)
|
||||||
{
|
{
|
||||||
if (ply != null && ply.Active)
|
if (ply != null && ply.Active && ply.FinishedHandshake)
|
||||||
{
|
{
|
||||||
if (displayIdsRequested)
|
if (displayIdsRequested)
|
||||||
if (ply.Account != null)
|
if (ply.Account != null)
|
||||||
|
|
|
||||||
|
|
@ -320,8 +320,8 @@ namespace TShockAPI.Configuration
|
||||||
[Description("The reason given if banning a mediumcore player on death.")]
|
[Description("The reason given if banning a mediumcore player on death.")]
|
||||||
public string MediumcoreBanReason = GetString("Death results in a ban");
|
public string MediumcoreBanReason = GetString("Death results in a ban");
|
||||||
|
|
||||||
/// <summary>Disbales IP bans by default, if no arguments are passed to the ban command.</summary>
|
/// <summary>Disables IP bans by default, if no arguments are passed to the ban command.</summary>
|
||||||
[Description("Disbales IP bans by default, if no arguments are passed to the ban command.")]
|
[Description("Disables IP bans by default, if no arguments are passed to the ban command.")]
|
||||||
public bool DisableDefaultIPBan;
|
public bool DisableDefaultIPBan;
|
||||||
|
|
||||||
/// <summary>Enable or disable the whitelist based on IP addresses in the whitelist.txt file.</summary>
|
/// <summary>Enable or disable the whitelist based on IP addresses in the whitelist.txt file.</summary>
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ namespace TShockAPI.DB
|
||||||
|
|
||||||
public PlayerData GetPlayerData(TSPlayer player, int acctid)
|
public PlayerData GetPlayerData(TSPlayer player, int acctid)
|
||||||
{
|
{
|
||||||
PlayerData playerData = new PlayerData(player);
|
PlayerData playerData = new PlayerData(false);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ using MySql.Data.MySqlClient;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using BCrypt.Net;
|
using BCrypt.Net;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using TShockAPI.Hooks;
|
||||||
|
|
||||||
namespace TShockAPI.DB
|
namespace TShockAPI.DB
|
||||||
{
|
{
|
||||||
|
|
@ -166,7 +167,41 @@ namespace TShockAPI.DB
|
||||||
if (null == grp)
|
if (null == grp)
|
||||||
throw new GroupNotExistsException(group);
|
throw new GroupNotExistsException(group);
|
||||||
|
|
||||||
if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", group, account.Name) == 0)
|
if (AccountHooks.OnAccountGroupUpdate(account, ref grp))
|
||||||
|
throw new UserGroupUpdateLockedException(account.Name);
|
||||||
|
|
||||||
|
if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", grp.Name, account.Name) == 0)
|
||||||
|
throw new UserAccountNotExistException(account.Name);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Update player group reference for any logged in player
|
||||||
|
foreach (var player in TShock.Players.Where(p => p != null && p.Account != null && p.Account.Name == account.Name))
|
||||||
|
{
|
||||||
|
player.Group = grp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new UserAccountManagerException(GetString("SetUserGroup SQL returned an error"), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the group for a given username
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="author">Who changes the group</param>
|
||||||
|
/// <param name="account">The user account</param>
|
||||||
|
/// <param name="group">The user account group to be set</param>
|
||||||
|
public void SetUserGroup(TSPlayer author, UserAccount account, string group)
|
||||||
|
{
|
||||||
|
Group grp = TShock.Groups.GetGroupByName(group);
|
||||||
|
if (null == grp)
|
||||||
|
throw new GroupNotExistsException(group);
|
||||||
|
|
||||||
|
if (AccountHooks.OnAccountGroupUpdate(account, author, ref grp))
|
||||||
|
throw new UserGroupUpdateLockedException(account.Name);
|
||||||
|
|
||||||
|
if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", grp.Name, account.Name) == 0)
|
||||||
throw new UserAccountNotExistException(account.Name);
|
throw new UserAccountNotExistException(account.Name);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -619,7 +654,7 @@ namespace TShockAPI.DB
|
||||||
public class UserAccountNotExistException : UserAccountManagerException
|
public class UserAccountNotExistException : UserAccountManagerException
|
||||||
{
|
{
|
||||||
/// <summary>Creates a new UserAccountNotExistException object, with the user account name in the message.</summary>
|
/// <summary>Creates a new UserAccountNotExistException object, with the user account name in the message.</summary>
|
||||||
/// <param name="name">The user account name to be pasesd in the message.</param>
|
/// <param name="name">The user account name to be passed in the message.</param>
|
||||||
/// <returns>A new UserAccountNotExistException object with a message containing the user account name that does not exist.</returns>
|
/// <returns>A new UserAccountNotExistException object with a message containing the user account name that does not exist.</returns>
|
||||||
public UserAccountNotExistException(string name)
|
public UserAccountNotExistException(string name)
|
||||||
: base(GetString($"User account {name} does not exist"))
|
: base(GetString($"User account {name} does not exist"))
|
||||||
|
|
@ -627,6 +662,20 @@ namespace TShockAPI.DB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>The UserGroupUpdateLockedException used when the user group update failed and the request failed as a result.</summary>.
|
||||||
|
[Serializable]
|
||||||
|
public class UserGroupUpdateLockedException : UserAccountManagerException
|
||||||
|
{
|
||||||
|
/// <summary>Creates a new UserGroupUpdateLockedException object.</summary>
|
||||||
|
/// <param name="name">The name of the user who failed to change the group.</param>
|
||||||
|
/// <returns>New UserGroupUpdateLockedException object with a message containing the name of the user account that failed to change the group.</returns>
|
||||||
|
public UserGroupUpdateLockedException(string name) :
|
||||||
|
base(GetString($"Unable to update group of user {name}."))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>A GroupNotExistsException, used when a group does not exist.</summary>
|
/// <summary>A GroupNotExistsException, used when a group does not exist.</summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class GroupNotExistsException : UserAccountManagerException
|
public class GroupNotExistsException : UserAccountManagerException
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ namespace TShockAPI
|
||||||
{ PacketTypes.TileSendSquare, HandleSendTileRect },
|
{ PacketTypes.TileSendSquare, HandleSendTileRect },
|
||||||
{ PacketTypes.ItemDrop, HandleItemDrop },
|
{ PacketTypes.ItemDrop, HandleItemDrop },
|
||||||
{ PacketTypes.ItemOwner, HandleItemOwner },
|
{ PacketTypes.ItemOwner, HandleItemOwner },
|
||||||
|
{ PacketTypes.NpcItemStrike, HandleNpcItemStrike },
|
||||||
{ PacketTypes.ProjectileNew, HandleProjectileNew },
|
{ PacketTypes.ProjectileNew, HandleProjectileNew },
|
||||||
{ PacketTypes.NpcStrike, HandleNpcStrike },
|
{ PacketTypes.NpcStrike, HandleNpcStrike },
|
||||||
{ PacketTypes.ProjectileDestroy, HandleProjectileKill },
|
{ PacketTypes.ProjectileDestroy, HandleProjectileKill },
|
||||||
|
|
@ -2619,9 +2620,9 @@ namespace TShockAPI
|
||||||
private static bool HandleConnecting(GetDataHandlerArgs args)
|
private static bool HandleConnecting(GetDataHandlerArgs args)
|
||||||
{
|
{
|
||||||
var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name);//
|
var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name);//
|
||||||
args.Player.DataWhenJoined = new PlayerData(args.Player);
|
args.Player.DataWhenJoined = new PlayerData(false);
|
||||||
args.Player.DataWhenJoined.CopyCharacter(args.Player);
|
args.Player.DataWhenJoined.CopyCharacter(args.Player);
|
||||||
args.Player.PlayerData = new PlayerData(args.Player);
|
args.Player.PlayerData = new PlayerData(false);
|
||||||
args.Player.PlayerData.CopyCharacter(args.Player);
|
args.Player.PlayerData.CopyCharacter(args.Player);
|
||||||
|
|
||||||
if (account != null && !TShock.Config.Settings.DisableUUIDLogin)
|
if (account != null && !TShock.Config.Settings.DisableUUIDLogin)
|
||||||
|
|
@ -2719,49 +2720,71 @@ namespace TShockAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
byte player = args.Data.ReadInt8();
|
byte player = args.Data.ReadInt8();
|
||||||
short spawnx = args.Data.ReadInt16();
|
short spawnX = args.Data.ReadInt16();
|
||||||
short spawny = args.Data.ReadInt16();
|
short spawnY = args.Data.ReadInt16();
|
||||||
int respawnTimer = args.Data.ReadInt32();
|
int respawnTimer = args.Data.ReadInt32();
|
||||||
short numberOfDeathsPVE = args.Data.ReadInt16();
|
short numberOfDeathsPVE = args.Data.ReadInt16();
|
||||||
short numberOfDeathsPVP = args.Data.ReadInt16();
|
short numberOfDeathsPVP = args.Data.ReadInt16();
|
||||||
PlayerSpawnContext context = (PlayerSpawnContext)args.Data.ReadByte();
|
PlayerSpawnContext context = (PlayerSpawnContext)args.Data.ReadByte();
|
||||||
|
|
||||||
if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context))
|
args.Player.FinishedHandshake = true;
|
||||||
|
|
||||||
|
if (OnPlayerSpawn(args.Player, args.Data, player, spawnX, spawnY, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if ((Main.ServerSideCharacter) && (spawnx == -1 && spawny == -1)) //this means they want to spawn to vanilla spawn
|
args.Player.Dead = respawnTimer > 0;
|
||||||
|
|
||||||
|
if (Main.ServerSideCharacter)
|
||||||
{
|
{
|
||||||
args.Player.sX = Main.spawnTileX;
|
// As long as the player has not changed his spawnpoint since initial connection,
|
||||||
args.Player.sY = Main.spawnTileY;
|
// we should not use the client's spawnpoint value. This is because the spawnpoint
|
||||||
args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48);
|
// value is not saved on the client when SSC is enabled. Hence, we have to assert
|
||||||
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force teleport 'vanilla spawn' {0}", args.Player.Name));
|
// the server-saved spawnpoint value until we can detect that the player has changed
|
||||||
|
// his spawn. Once we detect the spawnpoint changed, the client's spawnpoint value
|
||||||
|
// becomes the correct one to use.
|
||||||
|
//
|
||||||
|
// Note that spawnpoint changes (right-clicking beds) are not broadcasted to the
|
||||||
|
// server. Hence, the only way to detect spawnpoint changes is from the
|
||||||
|
// PlayerSpawn packet.
|
||||||
|
|
||||||
|
// handle initial connection
|
||||||
|
if (args.Player.State == 3)
|
||||||
|
{
|
||||||
|
// server saved spawnpoint value
|
||||||
|
args.Player.initialSpawn = true;
|
||||||
|
args.Player.initialServerSpawnX = args.TPlayer.SpawnX;
|
||||||
|
args.Player.initialServerSpawnY = args.TPlayer.SpawnY;
|
||||||
|
|
||||||
|
// initial client spawn point, do not use this to spawn the player
|
||||||
|
// we only use it to detect if the spawnpoint has changed during this session
|
||||||
|
args.Player.initialClientSpawnX = spawnX;
|
||||||
|
args.Player.initialClientSpawnY = spawnY;
|
||||||
|
|
||||||
|
// we first let the game handle completing the connection (state 3 => 10),
|
||||||
|
// then we will spawn the player at the saved spawnpoint in the next second,
|
||||||
|
// by reasserting the correct spawnpoint value
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0) && (args.TPlayer.SpawnX > 0) && ((args.TPlayer.SpawnX != args.Player.sX) && (args.TPlayer.SpawnY != args.Player.sY)))
|
// once we detect the client has changed his spawnpoint in the current session,
|
||||||
|
// the client spawnpoint value will be correct for the rest of the session
|
||||||
|
if (args.Player.spawnSynced || args.Player.initialClientSpawnX != spawnX || args.Player.initialClientSpawnY != spawnY)
|
||||||
{
|
{
|
||||||
args.Player.sX = args.TPlayer.SpawnX;
|
// Player has changed his spawnpoint, client and server TPlayer.Spawn{X,Y} is now synced
|
||||||
args.Player.sY = args.TPlayer.SpawnY;
|
args.Player.spawnSynced = true;
|
||||||
|
return false;
|
||||||
if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == TileID.Beds)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1)))
|
|
||||||
{
|
|
||||||
args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48);
|
|
||||||
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force teleport phase 1 {0}", args.Player.Name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0))
|
// the player has not changed his spawnpoint yet, so we assert the server-saved spawnpoint
|
||||||
{
|
// by teleporting the player instead of letting the game use the client's incorrect spawnpoint.
|
||||||
if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == TileID.Beds)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1)))
|
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force ssc teleport for {0} at ({1},{2})", args.Player.Name, args.TPlayer.SpawnX, args.TPlayer.SpawnY));
|
||||||
{
|
args.Player.TeleportSpawnpoint();
|
||||||
args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48);
|
|
||||||
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force teleport phase 2 {0}", args.Player.Name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (respawnTimer > 0)
|
args.TPlayer.respawnTimer = respawnTimer;
|
||||||
args.Player.Dead = true;
|
args.TPlayer.numberOfDeathsPVE = numberOfDeathsPVE;
|
||||||
else
|
args.TPlayer.numberOfDeathsPVP = numberOfDeathsPVP;
|
||||||
args.Player.Dead = false;
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2942,6 +2965,13 @@ namespace TShockAPI
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HandleNpcItemStrike(GetDataHandlerArgs args)
|
||||||
|
{
|
||||||
|
// Never sent by vanilla client, ignore this
|
||||||
|
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleNpcItemStrike surprise packet! Someone tell the TShock team! {0}", args.Player.Name));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool HandleProjectileNew(GetDataHandlerArgs args)
|
private static bool HandleProjectileNew(GetDataHandlerArgs args)
|
||||||
{
|
{
|
||||||
short ident = args.Data.ReadInt16();
|
short ident = args.Data.ReadInt16();
|
||||||
|
|
@ -4489,6 +4519,11 @@ namespace TShockAPI
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't modify the player data if it isn't there.
|
||||||
|
// This is the case whilst the player is connecting, as we receive the SyncLoadout packet before the ContinueConnecting2 packet.
|
||||||
|
if (args.Player.PlayerData == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
// The client does not sync slot changes when changing loadouts, it only tells the server the loadout index changed,
|
// The client does not sync slot changes when changing loadouts, it only tells the server the loadout index changed,
|
||||||
// and the server will replicate the changes the client did. This means that PlayerData.StoreSlot is never called, so we need to
|
// and the server will replicate the changes the client did. This means that PlayerData.StoreSlot is never called, so we need to
|
||||||
// swap around the PlayerData items ourself.
|
// swap around the PlayerData items ourself.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
namespace TShockAPI
|
namespace TShockAPI
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -52,17 +54,17 @@ namespace TShockAPI
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The group that this group inherits permissions from.
|
/// The group that this group inherits permissions from.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Group Parent { get; set; }
|
public virtual Group Parent { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The chat prefix for this group.
|
/// The chat prefix for this group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Prefix { get; set; }
|
public virtual string Prefix { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The chat suffix for this group.
|
/// The chat suffix for this group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Suffix { get; set; }
|
public virtual string Suffix { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the parent, not particularly sure why this is here.
|
/// The name of the parent, not particularly sure why this is here.
|
||||||
|
|
@ -164,6 +166,20 @@ namespace TShockAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte B = 255;
|
public byte B = 255;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simplifies work with the <see cref="R"/>, <see cref="G"/>, <see cref="B"/> properties.
|
||||||
|
/// </summary>
|
||||||
|
public virtual Color Color
|
||||||
|
{
|
||||||
|
get => new Color(R, G, B);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
R = value.R;
|
||||||
|
G = value.G;
|
||||||
|
B = value.B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default group attributed to unregistered users.
|
/// The default group attributed to unregistered users.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -242,7 +258,7 @@ namespace TShockAPI
|
||||||
/// Adds a permission to the list of negated permissions.
|
/// Adds a permission to the list of negated permissions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="permission">The permission to negate.</param>
|
/// <param name="permission">The permission to negate.</param>
|
||||||
public void NegatePermission(string permission)
|
public virtual void NegatePermission(string permission)
|
||||||
{
|
{
|
||||||
// Avoid duplicates
|
// Avoid duplicates
|
||||||
if (!negatedpermissions.Contains(permission))
|
if (!negatedpermissions.Contains(permission))
|
||||||
|
|
@ -256,7 +272,7 @@ namespace TShockAPI
|
||||||
/// Adds a permission to the list of permissions.
|
/// Adds a permission to the list of permissions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="permission">The permission to add.</param>
|
/// <param name="permission">The permission to add.</param>
|
||||||
public void AddPermission(string permission)
|
public virtual void AddPermission(string permission)
|
||||||
{
|
{
|
||||||
if (permission.StartsWith("!"))
|
if (permission.StartsWith("!"))
|
||||||
{
|
{
|
||||||
|
|
@ -276,7 +292,7 @@ namespace TShockAPI
|
||||||
/// will parse "!permission" and add it to the negated permissions.
|
/// will parse "!permission" and add it to the negated permissions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="permission">The new list of permissions to associate with the group.</param>
|
/// <param name="permission">The new list of permissions to associate with the group.</param>
|
||||||
public void SetPermission(List<string> permission)
|
public virtual void SetPermission(List<string> permission)
|
||||||
{
|
{
|
||||||
permissions.Clear();
|
permissions.Clear();
|
||||||
negatedpermissions.Clear();
|
negatedpermissions.Clear();
|
||||||
|
|
@ -288,7 +304,7 @@ namespace TShockAPI
|
||||||
/// where "!permission" will remove a negated permission.
|
/// where "!permission" will remove a negated permission.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="permission"></param>
|
/// <param name="permission"></param>
|
||||||
public void RemovePermission(string permission)
|
public virtual void RemovePermission(string permission)
|
||||||
{
|
{
|
||||||
if (permission.StartsWith("!"))
|
if (permission.StartsWith("!"))
|
||||||
{
|
{
|
||||||
|
|
@ -302,7 +318,7 @@ namespace TShockAPI
|
||||||
/// Assigns all fields of this instance to another.
|
/// Assigns all fields of this instance to another.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="otherGroup">The other instance.</param>
|
/// <param name="otherGroup">The other instance.</param>
|
||||||
public void AssignTo(Group otherGroup)
|
public virtual void AssignTo(Group otherGroup)
|
||||||
{
|
{
|
||||||
otherGroup.Name = Name;
|
otherGroup.Name = Name;
|
||||||
otherGroup.Parent = Parent;
|
otherGroup.Parent = Parent;
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,13 @@ namespace TShockAPI.Handlers
|
||||||
Removal,
|
Removal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum MatchResult
|
||||||
|
{
|
||||||
|
NotMatched,
|
||||||
|
RejectChanges,
|
||||||
|
BroadcastChanges,
|
||||||
|
}
|
||||||
|
|
||||||
private readonly int Width;
|
private readonly int Width;
|
||||||
private readonly int Height;
|
private readonly int Height;
|
||||||
|
|
||||||
|
|
@ -179,11 +186,11 @@ namespace TShockAPI.Handlers
|
||||||
/// <param name="player">The player the operation originates from.</param>
|
/// <param name="player">The player the operation originates from.</param>
|
||||||
/// <param name="rect">The tile rectangle of the operation.</param>
|
/// <param name="rect">The tile rectangle of the operation.</param>
|
||||||
/// <returns><see langword="true"/>, if the rect matches this operation and the changes have been applied, otherwise <see langword="false"/>.</returns>
|
/// <returns><see langword="true"/>, if the rect matches this operation and the changes have been applied, otherwise <see langword="false"/>.</returns>
|
||||||
public bool Matches(TSPlayer player, TileRect rect)
|
public MatchResult Matches(TSPlayer player, TileRect rect)
|
||||||
{
|
{
|
||||||
if (rect.Width != Width || rect.Height != Height)
|
if (rect.Width != Width || rect.Height != Height)
|
||||||
{
|
{
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int x = 0; x < rect.Width; x++)
|
for (int x = 0; x < rect.Width; x++)
|
||||||
|
|
@ -195,7 +202,7 @@ namespace TShockAPI.Handlers
|
||||||
{
|
{
|
||||||
if (tile.Type != TileType)
|
if (tile.Type != TileType)
|
||||||
{
|
{
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Type is MatchType.Placement or MatchType.StateChange)
|
if (Type is MatchType.Placement or MatchType.StateChange)
|
||||||
|
|
@ -204,7 +211,7 @@ namespace TShockAPI.Handlers
|
||||||
{
|
{
|
||||||
if (tile.FrameX < 0 || tile.FrameX > MaxFrameX || tile.FrameX % FrameXStep != 0)
|
if (tile.FrameX < 0 || tile.FrameX > MaxFrameX || tile.FrameX % FrameXStep != 0)
|
||||||
{
|
{
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (MaxFrameY != IGNORE_FRAME)
|
if (MaxFrameY != IGNORE_FRAME)
|
||||||
|
|
@ -214,7 +221,7 @@ namespace TShockAPI.Handlers
|
||||||
// this is the only tile type sent in a tile rect where the frame have a different pattern (56, 74, 92 instead of 54, 72, 90)
|
// this is the only tile type sent in a tile rect where the frame have a different pattern (56, 74, 92 instead of 54, 72, 90)
|
||||||
if (!(TileType == TileID.LunarMonolith && tile.FrameY % FrameYStep == 2))
|
if (!(TileType == TileID.LunarMonolith && tile.FrameY % FrameYStep == 2))
|
||||||
{
|
{
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -223,7 +230,7 @@ namespace TShockAPI.Handlers
|
||||||
{
|
{
|
||||||
if (tile.Active)
|
if (tile.Active)
|
||||||
{
|
{
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +243,7 @@ namespace TShockAPI.Handlers
|
||||||
if (!player.HasBuildPermission(x, y))
|
if (!player.HasBuildPermission(x, y))
|
||||||
{
|
{
|
||||||
// for simplicity, let's pretend that the edit was valid, but do not execute it
|
// for simplicity, let's pretend that the edit was valid, but do not execute it
|
||||||
return true;
|
return MatchResult.RejectChanges;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -257,10 +264,10 @@ namespace TShockAPI.Handlers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool MatchPlacement(TSPlayer player, TileRect rect)
|
private MatchResult MatchPlacement(TSPlayer player, TileRect rect)
|
||||||
{
|
{
|
||||||
for (int x = rect.X; x < rect.Y + rect.Width; x++)
|
for (int x = rect.X; x < rect.Y + rect.Width; x++)
|
||||||
{
|
{
|
||||||
|
|
@ -268,7 +275,7 @@ namespace TShockAPI.Handlers
|
||||||
{
|
{
|
||||||
if (Main.tile[x, y].active()) // the client will kill tiles that auto break before placing the object
|
if (Main.tile[x, y].active()) // the client will kill tiles that auto break before placing the object
|
||||||
{
|
{
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -277,7 +284,7 @@ namespace TShockAPI.Handlers
|
||||||
if (TShock.TileBans.TileIsBanned((short)TileType, player))
|
if (TShock.TileBans.TileIsBanned((short)TileType, player))
|
||||||
{
|
{
|
||||||
// for simplicity, let's pretend that the edit was valid, but do not execute it
|
// for simplicity, let's pretend that the edit was valid, but do not execute it
|
||||||
return true;
|
return MatchResult.RejectChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int x = 0; x < rect.Width; x++)
|
for (int x = 0; x < rect.Width; x++)
|
||||||
|
|
@ -291,10 +298,10 @@ namespace TShockAPI.Handlers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return MatchResult.BroadcastChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool MatchStateChange(TSPlayer player, TileRect rect)
|
private MatchResult MatchStateChange(TSPlayer player, TileRect rect)
|
||||||
{
|
{
|
||||||
for (int x = rect.X; x < rect.Y + rect.Width; x++)
|
for (int x = rect.X; x < rect.Y + rect.Width; x++)
|
||||||
{
|
{
|
||||||
|
|
@ -302,7 +309,7 @@ namespace TShockAPI.Handlers
|
||||||
{
|
{
|
||||||
if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType)
|
if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType)
|
||||||
{
|
{
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -322,18 +329,18 @@ namespace TShockAPI.Handlers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return MatchResult.BroadcastChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool MatchRemoval(TSPlayer player, TileRect rect)
|
private MatchResult MatchRemoval(TSPlayer player, TileRect rect)
|
||||||
{
|
{
|
||||||
for (int x = rect.X; x < rect.Y + rect.Width; x++)
|
for (int x = rect.X; x < rect.X + rect.Width; x++)
|
||||||
{
|
{
|
||||||
for (int y = rect.Y; y < rect.Y + rect.Height; y++)
|
for (int y = rect.Y; y < rect.Y + rect.Height; y++)
|
||||||
{
|
{
|
||||||
if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType)
|
if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType)
|
||||||
{
|
{
|
||||||
return false;
|
return MatchResult.NotMatched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -348,7 +355,7 @@ namespace TShockAPI.Handlers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return MatchResult.BroadcastChanges;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,7 +371,7 @@ namespace TShockAPI.Handlers
|
||||||
TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36, 18, 18),
|
TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36, 18, 18),
|
||||||
TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54, 18, 18),
|
TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54, 18, 18),
|
||||||
TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36, 18, 18),
|
TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36, 18, 18),
|
||||||
TileRectMatch.Placement(2, 3, TileID.HatRack, 90, 54, 18, 18),
|
TileRectMatch.Placement(3, 4, TileID.HatRack, 90, 54, 18, 18),
|
||||||
TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18, 18, 18),
|
TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18, 18, 18),
|
||||||
TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36, 18, 18),
|
TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36, 18, 18),
|
||||||
TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0, 18, 18),
|
TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0, 18, 18),
|
||||||
|
|
@ -436,7 +443,7 @@ namespace TShockAPI.Handlers
|
||||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from throttle from {args.Player.Name}"));
|
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from throttle from {args.Player.Name}"));
|
||||||
|
|
||||||
// send correcting data
|
// send correcting data
|
||||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -446,7 +453,7 @@ namespace TShockAPI.Handlers
|
||||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from being disabled from {args.Player.Name}"));
|
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from being disabled from {args.Player.Name}"));
|
||||||
|
|
||||||
// send correcting data
|
// send correcting data
|
||||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -468,7 +475,7 @@ namespace TShockAPI.Handlers
|
||||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
||||||
|
|
||||||
// send correcting data
|
// send correcting data
|
||||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,7 +485,7 @@ namespace TShockAPI.Handlers
|
||||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of range from {args.Player.Name}"));
|
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of range from {args.Player.Name}"));
|
||||||
|
|
||||||
// send correcting data
|
// send correcting data
|
||||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -488,19 +495,23 @@ namespace TShockAPI.Handlers
|
||||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
||||||
|
|
||||||
// send correcting data
|
// send correcting data
|
||||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the rect matches any valid operation
|
// check if the rect matches any valid operation
|
||||||
foreach (TileRectMatch match in Matches)
|
foreach (TileRectMatch match in Matches)
|
||||||
{
|
{
|
||||||
if (match.Matches(args.Player, rect))
|
var result = match.Matches(args.Player, rect);
|
||||||
|
if (result != TileRectMatch.MatchResult.NotMatched)
|
||||||
{
|
{
|
||||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
||||||
|
|
||||||
// send correcting data
|
// send correcting data
|
||||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
if (result == TileRectMatch.MatchResult.RejectChanges)
|
||||||
|
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
|
if (result == TileRectMatch.MatchResult.BroadcastChanges)
|
||||||
|
TSPlayer.All.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -511,14 +522,14 @@ namespace TShockAPI.Handlers
|
||||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
||||||
|
|
||||||
// send correcting data
|
// send correcting data
|
||||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from matches from {args.Player.Name}"));
|
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from matches from {args.Player.Name}"));
|
||||||
|
|
||||||
// send correcting data
|
// send correcting data
|
||||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
using TShockAPI.DB;
|
using TShockAPI.DB;
|
||||||
namespace TShockAPI.Hooks
|
namespace TShockAPI.Hooks
|
||||||
{
|
{
|
||||||
|
|
@ -39,6 +40,31 @@ namespace TShockAPI.Hooks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AccountGroupUpdateEventArgs : HandledEventArgs
|
||||||
|
{
|
||||||
|
public string AccountName { get; private set; }
|
||||||
|
public Group Group { get; set; }
|
||||||
|
|
||||||
|
public AccountGroupUpdateEventArgs(string accountName, Group group)
|
||||||
|
{
|
||||||
|
this.AccountName = accountName;
|
||||||
|
this.Group = group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AccountGroupUpdateByPlayerEventArgs : AccountGroupUpdateEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The player who updated the user's group
|
||||||
|
/// </summary>
|
||||||
|
public TSPlayer Player { get; private set; }
|
||||||
|
|
||||||
|
public AccountGroupUpdateByPlayerEventArgs(TSPlayer player, string accountName, Group group) : base(accountName, group)
|
||||||
|
{
|
||||||
|
this.Player = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class AccountHooks
|
public class AccountHooks
|
||||||
{
|
{
|
||||||
public delegate void AccountCreateD(AccountCreateEventArgs e);
|
public delegate void AccountCreateD(AccountCreateEventArgs e);
|
||||||
|
|
@ -62,5 +88,25 @@ namespace TShockAPI.Hooks
|
||||||
|
|
||||||
AccountDelete(new AccountDeleteEventArgs(u));
|
AccountDelete(new AccountDeleteEventArgs(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public delegate void AccountGroupUpdateD(AccountGroupUpdateEventArgs e);
|
||||||
|
public static event AccountGroupUpdateD AccountGroupUpdate;
|
||||||
|
|
||||||
|
public static bool OnAccountGroupUpdate(UserAccount account, TSPlayer author, ref Group group)
|
||||||
|
{
|
||||||
|
AccountGroupUpdateEventArgs args = new AccountGroupUpdateByPlayerEventArgs(author, account.Name, group);
|
||||||
|
AccountGroupUpdate?.Invoke(args);
|
||||||
|
group = args.Group;
|
||||||
|
|
||||||
|
return args.Handled;
|
||||||
|
}
|
||||||
|
public static bool OnAccountGroupUpdate(UserAccount account, ref Group group)
|
||||||
|
{
|
||||||
|
AccountGroupUpdateEventArgs args = new AccountGroupUpdateEventArgs(account.Name, group);
|
||||||
|
AccountGroupUpdate?.Invoke(args);
|
||||||
|
group = args.Group;
|
||||||
|
|
||||||
|
return args.Handled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using TShockAPI.DB;
|
using TShockAPI.DB;
|
||||||
|
|
@ -119,6 +120,49 @@ namespace TShockAPI.Hooks
|
||||||
public string CommandPrefix { get; set; }
|
public string CommandPrefix { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EventArgs used for the <see cref="PlayerHooks.PrePlayerCommand"/> event.
|
||||||
|
/// </summary>
|
||||||
|
public class PrePlayerCommandEventArgs : HandledEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The command entered by the player.
|
||||||
|
/// </summary>
|
||||||
|
public Command Command { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Command arguments.
|
||||||
|
/// </summary>
|
||||||
|
public CommandArgs Arguments { get; set; }
|
||||||
|
|
||||||
|
public PrePlayerCommandEventArgs(Command command, CommandArgs args)
|
||||||
|
{
|
||||||
|
Command = command;
|
||||||
|
Arguments = args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EventArgs used for the <see cref="PlayerHooks.PostPlayerCommand"/> event.
|
||||||
|
/// </summary>
|
||||||
|
public class PostPlayerCommandEventArgs : HandledEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The command entered by the player.
|
||||||
|
/// </summary>
|
||||||
|
public Command Command { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Command arguments.
|
||||||
|
/// </summary>
|
||||||
|
public CommandArgs Arguments { get; }
|
||||||
|
|
||||||
|
public PostPlayerCommandEventArgs(Command command, CommandArgs arguments, bool handled)
|
||||||
|
{
|
||||||
|
Command = command;
|
||||||
|
Arguments = arguments;
|
||||||
|
Handled = handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// EventArgs used for the <see cref="PlayerHooks.PlayerChat"/> event.
|
/// EventArgs used for the <see cref="PlayerHooks.PlayerChat"/> event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -343,6 +387,26 @@ namespace TShockAPI.Hooks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event PlayerCommandD PlayerCommand;
|
public static event PlayerCommandD PlayerCommand;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The delegate of the <see cref="PrePlayerCommand"/> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">The EventArgs for this event.</param>
|
||||||
|
public delegate void PrePlayerCommandD(PrePlayerCommandEventArgs e);
|
||||||
|
/// <summary>
|
||||||
|
/// Fired before a command is run.
|
||||||
|
/// </summary>
|
||||||
|
public static event PrePlayerCommandD PrePlayerCommand;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The delegate of the <see cref="PostPlayerCommand"/> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">The EventArgs for this event.</param>
|
||||||
|
public delegate void PostPlayerCommandD(PostPlayerCommandEventArgs e);
|
||||||
|
/// <summary>
|
||||||
|
/// Fired after a command is run.
|
||||||
|
/// </summary>
|
||||||
|
public static event PostPlayerCommandD PostPlayerCommand;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The delegate of the <see cref="PlayerChat"/> event.
|
/// The delegate of the <see cref="PlayerChat"/> event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -449,6 +513,40 @@ namespace TShockAPI.Hooks
|
||||||
return playerCommandEventArgs.Handled;
|
return playerCommandEventArgs.Handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fires the <see cref="PrePlayerCommand"/> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Command to be executed</param>
|
||||||
|
/// <param name="arguments">Command arguments</param>
|
||||||
|
/// <returns>True if the event has been handled.</returns>
|
||||||
|
public static bool OnPrePlayerCommand(Command cmd, ref CommandArgs arguments)
|
||||||
|
{
|
||||||
|
if (PrePlayerCommand == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PrePlayerCommandEventArgs args = new PrePlayerCommandEventArgs(cmd, arguments);
|
||||||
|
|
||||||
|
PrePlayerCommand(args);
|
||||||
|
|
||||||
|
arguments = args.Arguments;
|
||||||
|
return args.Handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fires the <see cref="PostPlayerCommand"/> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Executed command.</param>
|
||||||
|
/// <param name="arguments">Command arguments.</param>
|
||||||
|
/// <param name="handled">Is the command executed.</param>
|
||||||
|
public static void OnPostPlayerCommand(Command cmd, CommandArgs arguments, bool handled)
|
||||||
|
{
|
||||||
|
if (PostPlayerCommand == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PostPlayerCommandEventArgs args = new PostPlayerCommandEventArgs(cmd, arguments, handled);
|
||||||
|
PostPlayerCommand(args);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fires the <see cref="PlayerPreLogin"/> event.
|
/// Fires the <see cref="PlayerPreLogin"/> event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,12 @@ namespace TShockAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LanguageManager.Instance.ActiveCulture == GameCulture.DefaultCulture)
|
||||||
|
{
|
||||||
|
var bf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static;
|
||||||
|
// LanguageManager.SetLanguage will change this so we need to reset it back to null
|
||||||
|
typeof(CultureInfo).GetField("s_currentThreadUICulture", bf)?.SetValue(null, null);
|
||||||
|
}
|
||||||
return CultureInfo.CurrentUICulture;
|
return CultureInfo.CurrentUICulture;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Terraria;
|
using Terraria;
|
||||||
|
using Terraria.Initializers;
|
||||||
using Terraria.Localization;
|
using Terraria.Localization;
|
||||||
|
using Terraria.UI.Chat;
|
||||||
|
|
||||||
namespace TShockAPI.Localization
|
namespace TShockAPI.Localization
|
||||||
{
|
{
|
||||||
|
|
@ -37,6 +39,8 @@ namespace TShockAPI.Localization
|
||||||
|
|
||||||
private static readonly Dictionary<int, string> Buffs = new Dictionary<int, string>();
|
private static readonly Dictionary<int, string> Buffs = new Dictionary<int, string>();
|
||||||
|
|
||||||
|
private static readonly Dictionary<string,string> VanillaCommandsPrefixs = new Dictionary<string, string>();
|
||||||
|
|
||||||
internal static void Initialize()
|
internal static void Initialize()
|
||||||
{
|
{
|
||||||
var culture = Language.ActiveCulture;
|
var culture = Language.ActiveCulture;
|
||||||
|
|
@ -71,6 +75,15 @@ namespace TShockAPI.Localization
|
||||||
var i = (int)field.GetValue(null);
|
var i = (int)field.GetValue(null);
|
||||||
Prefixs.Add(i, Lang.prefix[i].Value);
|
Prefixs.Add(i, Lang.prefix[i].Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatInitializer.Load();
|
||||||
|
foreach (var command in ChatManager.Commands._localizedCommands)
|
||||||
|
{
|
||||||
|
if (VanillaCommandsPrefixs.ContainsKey(command.Value._name))
|
||||||
|
continue;
|
||||||
|
VanillaCommandsPrefixs.Add(command.Value._name,command.Key.Value);
|
||||||
|
}
|
||||||
|
ChatManager.Commands._localizedCommands.Clear();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -136,5 +149,18 @@ namespace TShockAPI.Localization
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get vanilla command prefix in English
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">vanilla command name</param>
|
||||||
|
/// <returns>vanilla command prefix in English</returns>
|
||||||
|
public static string GetCommandPrefixByName(string name)
|
||||||
|
{
|
||||||
|
string commandText;
|
||||||
|
if (VanillaCommandsPrefixs.TryGetValue(name, out commandText))
|
||||||
|
return commandText;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -155,13 +155,39 @@ namespace TShockAPI
|
||||||
/// <param name="netId">The net ID.</param>
|
/// <param name="netId">The net ID.</param>
|
||||||
/// <param name="stack">The stack.</param>
|
/// <param name="stack">The stack.</param>
|
||||||
/// <param name="prefixId">The prefix ID.</param>
|
/// <param name="prefixId">The prefix ID.</param>
|
||||||
public NetItem(int netId, int stack, byte prefixId)
|
public NetItem(int netId, int stack = 1, byte prefixId = 0)
|
||||||
{
|
{
|
||||||
_netId = netId;
|
_netId = netId;
|
||||||
_stack = stack;
|
_stack = stack;
|
||||||
_prefixId = prefixId;
|
_prefixId = prefixId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="NetItem"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">Item in the game.</param>
|
||||||
|
public NetItem(Item item)
|
||||||
|
{
|
||||||
|
_netId = item.netID;
|
||||||
|
_stack = item.stack;
|
||||||
|
_prefixId = item.prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates <see cref="Terraria.Item"/> based on data from this structure.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A copy of the item.</returns>
|
||||||
|
public Item ToItem()
|
||||||
|
{
|
||||||
|
Item item = new Item();
|
||||||
|
|
||||||
|
item.netDefaults(_netId);
|
||||||
|
item.stack = _stack;
|
||||||
|
item.prefix = _prefixId;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts the <see cref="NetItem"/> to a string.
|
/// Converts the <see cref="NetItem"/> to a string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -532,18 +532,9 @@ namespace TShockAPI
|
||||||
field.GetCustomAttributes(false).FirstOrDefault(o => o is DescriptionAttribute) as DescriptionAttribute;
|
field.GetCustomAttributes(false).FirstOrDefault(o => o is DescriptionAttribute) as DescriptionAttribute;
|
||||||
var desc = descattr != null && !string.IsNullOrWhiteSpace(descattr.Description) ? descattr.Description : GetString("No description available.");
|
var desc = descattr != null && !string.IsNullOrWhiteSpace(descattr.Description) ? descattr.Description : GetString("No description available.");
|
||||||
|
|
||||||
var commands = GetCommands(name);
|
var strs = GetCommands(name).Select(c => c.Names.Count > 1
|
||||||
foreach (var c in commands)
|
? $"/{c.Name} (/{string.Join(" /", c.Names.Skip(1))})"
|
||||||
{
|
: $"/{c.Name}");
|
||||||
for (var i = 0; i < c.Names.Count; i++)
|
|
||||||
{
|
|
||||||
c.Names[i] = "/" + c.Names[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var strs =
|
|
||||||
commands.Select(
|
|
||||||
c =>
|
|
||||||
c.Name + (c.Names.Count > 1 ? " ({0})".SFormat(string.Join(" ", c.Names.ToArray(), 1, c.Names.Count - 1)) : ""));
|
|
||||||
|
|
||||||
sb.AppendLine($"## {name}");
|
sb.AppendLine($"## {name}");
|
||||||
sb.AppendLine($"{desc}");
|
sb.AppendLine($"{desc}");
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ using Terraria.Localization;
|
||||||
using Terraria.GameContent.NetModules;
|
using Terraria.GameContent.NetModules;
|
||||||
using Terraria.Net;
|
using Terraria.Net;
|
||||||
using Terraria.ID;
|
using Terraria.ID;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace TShockAPI
|
namespace TShockAPI
|
||||||
{
|
{
|
||||||
|
|
@ -63,13 +64,22 @@ namespace TShockAPI
|
||||||
public int unlockedSuperCart;
|
public int unlockedSuperCart;
|
||||||
public int enabledSuperCart;
|
public int enabledSuperCart;
|
||||||
|
|
||||||
public PlayerData(TSPlayer player)
|
/// <summary>
|
||||||
|
/// Sets the default values for the inventory.
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("The player argument is not used.")]
|
||||||
|
public PlayerData(TSPlayer player) : this(true) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the default values for the inventory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="includingStarterInventory">Is it necessary to load items from TShock's config</param>
|
||||||
|
public PlayerData(bool includingStarterInventory = true)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < NetItem.MaxInventory; i++)
|
for (int i = 0; i < NetItem.MaxInventory; i++)
|
||||||
{
|
|
||||||
this.inventory[i] = new NetItem();
|
this.inventory[i] = new NetItem();
|
||||||
}
|
|
||||||
|
|
||||||
|
if (includingStarterInventory)
|
||||||
for (int i = 0; i < TShock.ServerSideCharacterConfig.Settings.StartingInventory.Count; i++)
|
for (int i = 0; i < TShock.ServerSideCharacterConfig.Settings.StartingInventory.Count; i++)
|
||||||
{
|
{
|
||||||
var item = TShock.ServerSideCharacterConfig.Settings.StartingInventory[i];
|
var item = TShock.ServerSideCharacterConfig.Settings.StartingInventory[i];
|
||||||
|
|
@ -86,12 +96,22 @@ namespace TShockAPI
|
||||||
/// <param name="stack"></param>
|
/// <param name="stack"></param>
|
||||||
public void StoreSlot(int slot, int netID, byte prefix, int stack)
|
public void StoreSlot(int slot, int netID, byte prefix, int stack)
|
||||||
{
|
{
|
||||||
if (slot > (this.inventory.Length - 1)) //if the slot is out of range then dont save
|
StoreSlot(slot, new NetItem(netID, stack, prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores an item at the specific storage slot
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot"></param>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
public void StoreSlot(int slot, NetItem item)
|
||||||
|
{
|
||||||
|
if (slot > (this.inventory.Length - 1) || slot < 0) //if the slot is out of range then dont save
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inventory[slot] = new NetItem(netID, stack, prefix);
|
this.inventory[slot] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -104,16 +124,8 @@ namespace TShockAPI
|
||||||
this.maxHealth = player.TPlayer.statLifeMax;
|
this.maxHealth = player.TPlayer.statLifeMax;
|
||||||
this.mana = player.TPlayer.statMana;
|
this.mana = player.TPlayer.statMana;
|
||||||
this.maxMana = player.TPlayer.statManaMax;
|
this.maxMana = player.TPlayer.statManaMax;
|
||||||
if (player.sX > 0 && player.sY > 0)
|
|
||||||
{
|
|
||||||
this.spawnX = player.sX;
|
|
||||||
this.spawnY = player.sY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.spawnX = player.TPlayer.SpawnX;
|
this.spawnX = player.TPlayer.SpawnX;
|
||||||
this.spawnY = player.TPlayer.SpawnY;
|
this.spawnY = player.TPlayer.SpawnY;
|
||||||
}
|
|
||||||
extraSlot = player.TPlayer.extraAccessory ? 1 : 0;
|
extraSlot = player.TPlayer.extraAccessory ? 1 : 0;
|
||||||
this.skinVariant = player.TPlayer.skinVariant;
|
this.skinVariant = player.TPlayer.skinVariant;
|
||||||
this.hair = player.TPlayer.hair;
|
this.hair = player.TPlayer.hair;
|
||||||
|
|
@ -266,8 +278,6 @@ namespace TShockAPI
|
||||||
player.TPlayer.statManaMax = this.maxMana;
|
player.TPlayer.statManaMax = this.maxMana;
|
||||||
player.TPlayer.SpawnX = this.spawnX;
|
player.TPlayer.SpawnX = this.spawnX;
|
||||||
player.TPlayer.SpawnY = this.spawnY;
|
player.TPlayer.SpawnY = this.spawnY;
|
||||||
player.sX = this.spawnX;
|
|
||||||
player.sY = this.spawnY;
|
|
||||||
player.TPlayer.hairDye = this.hairDye;
|
player.TPlayer.hairDye = this.hairDye;
|
||||||
player.TPlayer.anglerQuestsFinished = this.questsCompleted;
|
player.TPlayer.anglerQuestsFinished = this.questsCompleted;
|
||||||
player.TPlayer.UsingBiomeTorches = this.usingBiomeTorches == 1;
|
player.TPlayer.UsingBiomeTorches = this.usingBiomeTorches == 1;
|
||||||
|
|
|
||||||
|
|
@ -351,7 +351,6 @@ namespace Rests
|
||||||
{
|
{
|
||||||
str = string.Format("{0}({1});", jsonp, str);
|
str = string.Format("{0}({1});", jsonp, str);
|
||||||
}
|
}
|
||||||
e.Response.Connection.Type = ConnectionType.Close;
|
|
||||||
e.Response.ContentType = new ContentTypeHeader("application/json; charset=utf-8");
|
e.Response.ContentType = new ContentTypeHeader("application/json; charset=utf-8");
|
||||||
e.Response.Add(serverHeader);
|
e.Response.Add(serverHeader);
|
||||||
var bytes = Encoding.UTF8.GetBytes(str);
|
var bytes = Encoding.UTF8.GetBytes(str);
|
||||||
|
|
|
||||||
|
|
@ -402,7 +402,7 @@ namespace TShockAPI
|
||||||
{"serverversion", Main.versionNumber},
|
{"serverversion", Main.versionNumber},
|
||||||
{"tshockversion", TShock.VersionNum},
|
{"tshockversion", TShock.VersionNum},
|
||||||
{"port", TShock.Config.Settings.ServerPort},
|
{"port", TShock.Config.Settings.ServerPort},
|
||||||
{"playercount", Main.player.Where(p => null != p && p.active).Count()},
|
{"playercount", TShock.Utils.GetActivePlayerCount()},
|
||||||
{"maxplayers", TShock.Config.Settings.MaxSlots},
|
{"maxplayers", TShock.Config.Settings.MaxSlots},
|
||||||
{"world", (TShock.Config.Settings.UseServerName ? TShock.Config.Settings.ServerName : Main.worldName)},
|
{"world", (TShock.Config.Settings.UseServerName ? TShock.Config.Settings.ServerName : Main.worldName)},
|
||||||
{"uptime", (DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime).ToString(@"d'.'hh':'mm':'ss")},
|
{"uptime", (DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime).ToString(@"d'.'hh':'mm':'ss")},
|
||||||
|
|
@ -555,7 +555,8 @@ namespace TShockAPI
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TShock.UserAccounts.SetUserGroup(account, group);
|
TShock.UserAccounts.SetUserGroup(new TSRestPlayer(args.TokenData.Username, TShock.Groups.GetGroupByName(args.TokenData.UserGroupName)),
|
||||||
|
account, group);
|
||||||
response.Add("group-response", "Group updated successfully");
|
response.Add("group-response", "Group updated successfully");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
@ -944,8 +945,8 @@ namespace TShockAPI
|
||||||
[Token]
|
[Token]
|
||||||
private object PlayerList(RestRequestArgs args)
|
private object PlayerList(RestRequestArgs args)
|
||||||
{
|
{
|
||||||
var activeplayers = Main.player.Where(p => null != p && p.active).ToList();
|
var activeplayers = TShock.Players.Where(p => null != p && p.Active).Select(p => p.Name);
|
||||||
return new RestObject() { { "players", string.Join(", ", activeplayers.Select(p => p.name)) } };
|
return new RestObject() { { "players", string.Join(", ", activeplayers) } };
|
||||||
}
|
}
|
||||||
|
|
||||||
[Description("Fetches detailed user information on all connected users, and can be filtered by specifying a key value pair filter users where the key is a field and the value is a users field value.")]
|
[Description("Fetches detailed user information on all connected users, and can be filtered by specifying a key value pair filter users where the key is a field and the value is a users field value.")]
|
||||||
|
|
|
||||||
|
|
@ -177,8 +177,13 @@ namespace TShockAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int RPPending = 0;
|
public int RPPending = 0;
|
||||||
|
|
||||||
public int sX = -1;
|
|
||||||
public int sY = -1;
|
public bool initialSpawn = false;
|
||||||
|
public int initialServerSpawnX = -2;
|
||||||
|
public int initialServerSpawnY = -2;
|
||||||
|
public bool spawnSynced = false;
|
||||||
|
public int initialClientSpawnX = -2;
|
||||||
|
public int initialClientSpawnY = -2;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A queue of tiles destroyed by the player for reverting.
|
/// A queue of tiles destroyed by the player for reverting.
|
||||||
|
|
@ -351,6 +356,9 @@ namespace TShockAPI
|
||||||
/// <summary>Determines if the player is disabled for not clearing their trash. A re-login is the only way to reset this.</summary>
|
/// <summary>Determines if the player is disabled for not clearing their trash. A re-login is the only way to reset this.</summary>
|
||||||
public bool IsDisabledPendingTrashRemoval;
|
public bool IsDisabledPendingTrashRemoval;
|
||||||
|
|
||||||
|
/// <summary>Determines if the player has finished the handshake (Sent all necessary packets for connection, such as Request World Data, Spawn Player, etc). A normal client would do all of this no problem.</summary>
|
||||||
|
public bool FinishedHandshake = false;
|
||||||
|
|
||||||
/// <summary>Checks to see if active throttling is happening on events by Bouncer. Rejects repeated events by malicious clients in a short window.</summary>
|
/// <summary>Checks to see if active throttling is happening on events by Bouncer. Rejects repeated events by malicious clients in a short window.</summary>
|
||||||
/// <returns>If the player is currently being throttled by Bouncer, or not.</returns>
|
/// <returns>If the player is currently being throttled by Bouncer, or not.</returns>
|
||||||
public bool IsBouncerThrottled()
|
public bool IsBouncerThrottled()
|
||||||
|
|
@ -1284,7 +1292,7 @@ namespace TShockAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerData = new PlayerData(this);
|
PlayerData = new PlayerData();
|
||||||
Group = TShock.Groups.GetGroupByName(TShock.Config.Settings.DefaultGuestGroupName);
|
Group = TShock.Groups.GetGroupByName(TShock.Config.Settings.DefaultGuestGroupName);
|
||||||
tempGroup = null;
|
tempGroup = null;
|
||||||
if (tempGroupTimer != null)
|
if (tempGroupTimer != null)
|
||||||
|
|
@ -1380,6 +1388,25 @@ namespace TShockAPI
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Teleports the player to their spawnpoint.
|
||||||
|
/// Teleports to main spawnpoint if their bed is not active.
|
||||||
|
/// Supports SSC.
|
||||||
|
/// </summary>
|
||||||
|
public bool TeleportSpawnpoint()
|
||||||
|
{
|
||||||
|
// NOTE: it is vanilla behaviour to not permanently override the spawnpoint if the bed spawn is broken/invalid
|
||||||
|
int x = TPlayer.SpawnX;
|
||||||
|
int y = TPlayer.SpawnY;
|
||||||
|
if ((x == -1 && y == -1) ||
|
||||||
|
!Main.tile[x, y - 1].active() || Main.tile[x, y - 1].type != TileID.Beds || !WorldGen.StartRoomCheck(x, y - 1))
|
||||||
|
{
|
||||||
|
x = Main.spawnTileX;
|
||||||
|
y = Main.spawnTileY;
|
||||||
|
}
|
||||||
|
return Teleport(x * 16, y * 16 - 48);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Heals the player.
|
/// Heals the player.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1393,16 +1420,9 @@ namespace TShockAPI
|
||||||
/// Spawns the player at his spawn point.
|
/// Spawns the player at his spawn point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Spawn(PlayerSpawnContext context, int? respawnTimer = null)
|
public void Spawn(PlayerSpawnContext context, int? respawnTimer = null)
|
||||||
{
|
|
||||||
if (this.sX > 0 && this.sY > 0)
|
|
||||||
{
|
|
||||||
Spawn(this.sX, this.sY, context, respawnTimer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
Spawn(TPlayer.SpawnX, TPlayer.SpawnY, context, respawnTimer);
|
Spawn(TPlayer.SpawnX, TPlayer.SpawnY, context, respawnTimer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Spawns the player at the given coordinates.
|
/// Spawns the player at the given coordinates.
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ namespace TShockAPI
|
||||||
/// <summary>VersionNum - The version number the TerrariaAPI will return back to the API. We just use the Assembly info.</summary>
|
/// <summary>VersionNum - The version number the TerrariaAPI will return back to the API. We just use the Assembly info.</summary>
|
||||||
public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version;
|
public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
/// <summary>VersionCodename - The version codename is displayed when the server starts. Inspired by software codenames conventions.</summary>
|
/// <summary>VersionCodename - The version codename is displayed when the server starts. Inspired by software codenames conventions.</summary>
|
||||||
public static readonly string VersionCodename = "Intensity";
|
public static readonly string VersionCodename = "East";
|
||||||
|
|
||||||
/// <summary>SavePath - This is the path TShock saves its data in. This path is relative to the TerrariaServer.exe (not in ServerPlugins).</summary>
|
/// <summary>SavePath - This is the path TShock saves its data in. This path is relative to the TerrariaServer.exe (not in ServerPlugins).</summary>
|
||||||
public static string SavePath = "tshock";
|
public static string SavePath = "tshock";
|
||||||
|
|
@ -428,6 +428,8 @@ namespace TShockAPI
|
||||||
Hooks.AccountHooks.AccountDelete += OnAccountDelete;
|
Hooks.AccountHooks.AccountDelete += OnAccountDelete;
|
||||||
Hooks.AccountHooks.AccountCreate += OnAccountCreate;
|
Hooks.AccountHooks.AccountCreate += OnAccountCreate;
|
||||||
|
|
||||||
|
On.Terraria.RemoteClient.Reset += RemoteClient_Reset;
|
||||||
|
|
||||||
GetDataHandlers.InitGetDataHandler();
|
GetDataHandlers.InitGetDataHandler();
|
||||||
Commands.InitCommands();
|
Commands.InitCommands();
|
||||||
|
|
||||||
|
|
@ -496,6 +498,12 @@ namespace TShockAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RemoteClient_Reset(On.Terraria.RemoteClient.orig_Reset orig, RemoteClient client)
|
||||||
|
{
|
||||||
|
client.ClientUUID = null;
|
||||||
|
orig(client);
|
||||||
|
}
|
||||||
|
|
||||||
private static void OnAchievementInitializerLoad(ILContext il)
|
private static void OnAchievementInitializerLoad(ILContext il)
|
||||||
{
|
{
|
||||||
// Modify AchievementInitializer.Load to remove the Main.netMode == 2 check (occupies the first 4 IL instructions)
|
// Modify AchievementInitializer.Load to remove the Main.netMode == 2 check (occupies the first 4 IL instructions)
|
||||||
|
|
@ -1174,16 +1182,16 @@ namespace TShockAPI
|
||||||
if (player.RecentFuse > 0)
|
if (player.RecentFuse > 0)
|
||||||
player.RecentFuse--;
|
player.RecentFuse--;
|
||||||
|
|
||||||
if ((Main.ServerSideCharacter) && (player.TPlayer.SpawnX > 0) && (player.sX != player.TPlayer.SpawnX))
|
if (Main.ServerSideCharacter && player.initialSpawn)
|
||||||
{
|
{
|
||||||
player.sX = player.TPlayer.SpawnX;
|
player.initialSpawn = false;
|
||||||
player.sY = player.TPlayer.SpawnY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((Main.ServerSideCharacter) && (player.sX > 0) && (player.sY > 0) && (player.TPlayer.SpawnX < 0))
|
// reassert the correct spawnpoint value after the game's Spawn handler changed it
|
||||||
{
|
player.TPlayer.SpawnX = player.initialServerSpawnX;
|
||||||
player.TPlayer.SpawnX = player.sX;
|
player.TPlayer.SpawnY = player.initialServerSpawnY;
|
||||||
player.TPlayer.SpawnY = player.sY;
|
|
||||||
|
player.TeleportSpawnpoint();
|
||||||
|
TShock.Log.ConsoleDebug(GetString("OnSecondUpdate / initial ssc spawn for {0} at ({1}, {2})", player.Name, player.TPlayer.SpawnX, player.TPlayer.SpawnY));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.RPPending > 0)
|
if (player.RPPending > 0)
|
||||||
|
|
@ -1368,6 +1376,8 @@ namespace TShockAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bans.CheckBan(player);
|
||||||
Players[args.Who] = player;
|
Players[args.Who] = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1389,7 +1399,8 @@ namespace TShockAPI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans.CheckBan(player);
|
if (Bans.CheckBan(player))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>OnLeave - Called when a player leaves the server.</summary>
|
/// <summary>OnLeave - Called when a player leaves the server.</summary>
|
||||||
|
|
@ -1429,7 +1440,7 @@ namespace TShockAPI
|
||||||
|
|
||||||
if (tsplr.ReceivedInfo)
|
if (tsplr.ReceivedInfo)
|
||||||
{
|
{
|
||||||
if (!tsplr.SilentKickInProgress && tsplr.State >= 3)
|
if (!tsplr.SilentKickInProgress && tsplr.State >= 3 && tsplr.FinishedHandshake) //The player has left, do not broadcast any clients exploiting the behaviour of not spawning their player.
|
||||||
Utils.Broadcast(GetString("{0} has left.", tsplr.Name), Color.Yellow);
|
Utils.Broadcast(GetString("{0} has left.", tsplr.Name), Color.Yellow);
|
||||||
Log.Info(GetString("{0} disconnected.", tsplr.Name));
|
Log.Info(GetString("{0} disconnected.", tsplr.Name));
|
||||||
|
|
||||||
|
|
@ -1450,6 +1461,9 @@ namespace TShockAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tsplr.FinishedHandshake = false;
|
||||||
|
|
||||||
// Fire the OnPlayerLogout hook too, if the player was logged in and they have a TSPlayer object.
|
// Fire the OnPlayerLogout hook too, if the player was logged in and they have a TSPlayer object.
|
||||||
if (tsplr.IsLoggedIn)
|
if (tsplr.IsLoggedIn)
|
||||||
{
|
{
|
||||||
|
|
@ -1479,6 +1493,12 @@ namespace TShockAPI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tsplr.FinishedHandshake)
|
||||||
|
{
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.Text.Length > 500)
|
if (args.Text.Length > 500)
|
||||||
{
|
{
|
||||||
tsplr.Kick(GetString("Crash attempt via long chat packet."), true);
|
tsplr.Kick(GetString("Crash attempt via long chat packet."), true);
|
||||||
|
|
@ -1497,11 +1517,11 @@ namespace TShockAPI
|
||||||
{
|
{
|
||||||
if (!String.IsNullOrEmpty(text))
|
if (!String.IsNullOrEmpty(text))
|
||||||
{
|
{
|
||||||
text = item.Key.Value + ' ' + text;
|
text = EnglishLanguage.GetCommandPrefixByName(item.Value._name) + ' ' + text;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
text = item.Key.Value;
|
text = EnglishLanguage.GetCommandPrefixByName(item.Value._name);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1695,14 +1715,14 @@ namespace TShockAPI
|
||||||
Log.Info(GetString("{0} ({1}) from '{2}' group from '{3}' joined. ({4}/{5})", player.Name, player.IP,
|
Log.Info(GetString("{0} ({1}) from '{2}' group from '{3}' joined. ({4}/{5})", player.Name, player.IP,
|
||||||
player.Group.Name, player.Country, TShock.Utils.GetActivePlayerCount(),
|
player.Group.Name, player.Country, TShock.Utils.GetActivePlayerCount(),
|
||||||
TShock.Config.Settings.MaxSlots));
|
TShock.Config.Settings.MaxSlots));
|
||||||
if (!player.SilentJoinInProgress)
|
if (!player.SilentJoinInProgress && player.FinishedHandshake)
|
||||||
Utils.Broadcast(GetString("{0} ({1}) has joined.", player.Name, player.Country), Color.Yellow);
|
Utils.Broadcast(GetString("{0} ({1}) has joined.", player.Name, player.Country), Color.Yellow);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.Info(GetString("{0} ({1}) from '{2}' group joined. ({3}/{4})", player.Name, player.IP,
|
Log.Info(GetString("{0} ({1}) from '{2}' group joined. ({3}/{4})", player.Name, player.IP,
|
||||||
player.Group.Name, TShock.Utils.GetActivePlayerCount(), TShock.Config.Settings.MaxSlots));
|
player.Group.Name, TShock.Utils.GetActivePlayerCount(), TShock.Config.Settings.MaxSlots));
|
||||||
if (!player.SilentJoinInProgress)
|
if (!player.SilentJoinInProgress && player.FinishedHandshake)
|
||||||
Utils.Broadcast(GetString("{0} has joined.", player.Name), Color.Yellow);
|
Utils.Broadcast(GetString("{0} has joined.", player.Name), Color.Yellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
Also, be sure to release on github with the exact assembly version tag as below
|
Also, be sure to release on github with the exact assembly version tag as below
|
||||||
so that the update manager works correctly (via the Github releases api and mimic)
|
so that the update manager works correctly (via the Github releases api and mimic)
|
||||||
-->
|
-->
|
||||||
<Version>5.2.0</Version>
|
<Version>5.2.2</Version>
|
||||||
<AssemblyTitle>TShock for Terraria</AssemblyTitle>
|
<AssemblyTitle>TShock for Terraria</AssemblyTitle>
|
||||||
<Company>Pryaxis & TShock Contributors</Company>
|
<Company>Pryaxis & TShock Contributors</Company>
|
||||||
<Product>TShockAPI</Product>
|
<Product>TShockAPI</Product>
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||||
<PackageReference Include="GetText.NET" Version="1.7.14" />
|
<PackageReference Include="GetText.NET" Version="1.7.14" />
|
||||||
<PackageReference Include="MySql.Data" Version="8.0.31" />
|
<PackageReference Include="MySql.Data" Version="8.4.0" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.11" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.11" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ namespace TShockAPI
|
||||||
foreach (TSPlayer player in TShock.Players)
|
foreach (TSPlayer player in TShock.Players)
|
||||||
{
|
{
|
||||||
if (player != null && player != excludedPlayer && player.Active && player.HasPermission(Permissions.logs) &&
|
if (player != null && player != excludedPlayer && player.Active && player.HasPermission(Permissions.logs) &&
|
||||||
player.DisplayLogs && TShock.Config.Settings.DisableSpewLogs == false)
|
player.DisplayLogs && !TShock.Config.Settings.DisableSpewLogs)
|
||||||
player.SendMessage(log, color);
|
player.SendMessage(log, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +183,7 @@ namespace TShockAPI
|
||||||
/// <returns>The number of active players on the server.</returns>
|
/// <returns>The number of active players on the server.</returns>
|
||||||
public int GetActivePlayerCount()
|
public int GetActivePlayerCount()
|
||||||
{
|
{
|
||||||
return Main.player.Where(p => null != p && p.active).Count();
|
return TShock.Players.Count(p => null != p && p.Active && p.FinishedHandshake);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Random should not be generated in a method
|
//Random should not be generated in a method
|
||||||
|
|
@ -1149,11 +1149,15 @@ namespace TShockAPI
|
||||||
/// <param name="empty">If the server is empty; determines if we should use Utils.GetActivePlayerCount() for player count or 0.</param>
|
/// <param name="empty">If the server is empty; determines if we should use Utils.GetActivePlayerCount() for player count or 0.</param>
|
||||||
internal void SetConsoleTitle(bool empty)
|
internal void SetConsoleTitle(bool empty)
|
||||||
{
|
{
|
||||||
|
if (ShouldSkipTitle)
|
||||||
|
return;
|
||||||
Console.Title = GetString("{0}{1}/{2} on {3} @ {4}:{5} (TShock for Terraria v{6})",
|
Console.Title = GetString("{0}{1}/{2} on {3} @ {4}:{5} (TShock for Terraria v{6})",
|
||||||
!string.IsNullOrWhiteSpace(TShock.Config.Settings.ServerName) ? TShock.Config.Settings.ServerName + " - " : "",
|
!string.IsNullOrWhiteSpace(TShock.Config.Settings.ServerName) ? TShock.Config.Settings.ServerName + " - " : "",
|
||||||
empty ? 0 : GetActivePlayerCount(),
|
empty ? 0 : GetActivePlayerCount(),
|
||||||
TShock.Config.Settings.MaxSlots, Main.worldName, Netplay.ServerIP.ToString(), Netplay.ListenPort, TShock.VersionNum);
|
TShock.Config.Settings.MaxSlots, Main.worldName, Netplay.ServerIP.ToString(), Netplay.ListenPort, TShock.VersionNum);
|
||||||
}
|
}
|
||||||
|
// Some terminals doesn't supports XTerm escape sequences for setting the title
|
||||||
|
private static bool ShouldSkipTitle = !System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows) && !(Environment.GetEnvironmentVariable("TERM")?.Contains("xterm") ?? false);
|
||||||
|
|
||||||
/// <summary>Determines the distance between two vectors.</summary>
|
/// <summary>Determines the distance between two vectors.</summary>
|
||||||
/// <param name="value1">The first vector location.</param>
|
/// <param name="value1">The first vector location.</param>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,6 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="SharpZipLib" Version="1.4.1" />
|
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using TShockPluginManager;
|
using TShockPluginManager;
|
||||||
|
|
||||||
|
// On occasion, users have been seen extracting TShock into their client installation directory -- this is of course incorrect, and is known
|
||||||
|
// to cause issues. Let's attempt to catch this before anything happens (specifically, before Terraria assemblies are resolved) and prevent
|
||||||
|
// TShock from launching.
|
||||||
|
if (File.Exists("TerrariaServer.exe"))
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.Error.WriteLine("A \"TerrariaServer.exe\" file has been found in the current working directory.");
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
"This indicates either installation into a Terraria client directory, or installation into a legacy (TShock 4 or older) TShock directory.");
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
"TShock is never to be installed inside a Terraria client directory. You should instead extract your TShock installation into it's own directory.");
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
"If you are updating a legacy TShock installation, please follow the following documentation to update: https://ikebukuro.tshock.co/#/?id=upgrading-from-tshock-4");
|
||||||
|
Console.Error.WriteLine("The launcher will now exit.");
|
||||||
|
Console.ResetColor();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.Length > 0 && args[0].ToLower() == "plugins")
|
if (args.Length > 0 && args[0].ToLower() == "plugins")
|
||||||
{
|
{
|
||||||
var items = args.ToList();
|
var items = args.ToList();
|
||||||
items.RemoveAt(0);
|
items.RemoveAt(0);
|
||||||
await NugetCLI.Main(items);
|
await NugetCLI.Main(items);
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -42,7 +60,7 @@ Dictionary<string, Assembly> _cache = new Dictionary<string, Assembly>();
|
||||||
|
|
||||||
System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += Default_Resolving;
|
System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += Default_Resolving;
|
||||||
|
|
||||||
Start();
|
return Start();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a module from the ./bin folder, either with a .dll by preference or .exe
|
/// Resolves a module from the ./bin folder, either with a .dll by preference or .exe
|
||||||
|
|
@ -70,7 +88,8 @@ Assembly? Default_Resolving(System.Runtime.Loader.AssemblyLoadContext arg1, Asse
|
||||||
/// Initiates the TSAPI server.
|
/// Initiates the TSAPI server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This method exists so that the resolver can attach before TSAPI needs its dependencies.</remarks>
|
/// <remarks>This method exists so that the resolver can attach before TSAPI needs its dependencies.</remarks>
|
||||||
void Start()
|
int Start()
|
||||||
{
|
{
|
||||||
TerrariaApi.Server.Program.Main(args);
|
TerrariaApi.Server.Program.Main(args);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||||
<PackageReference Include="MySql.Data" Version="8.0.31" />
|
<PackageReference Include="MySql.Data" Version="8.4.0" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.11" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.11" />
|
||||||
<PackageReference Include="ModFramework" Version="1.1.7" GeneratePathProperty="true" /> <!-- only used to extract out to ./bin. -->
|
<PackageReference Include="ModFramework" Version="1.1.7" GeneratePathProperty="true" /> <!-- only used to extract out to ./bin. -->
|
||||||
<PackageReference Include="GetText.NET" Version="1.7.14" /> <!-- only used to extract out to ./bin. -->
|
<PackageReference Include="GetText.NET" Version="1.7.14" /> <!-- only used to extract out to ./bin. -->
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ using NuGet.Versioning;
|
||||||
|
|
||||||
namespace TShockPluginManager
|
namespace TShockPluginManager
|
||||||
{
|
{
|
||||||
|
|
||||||
public class Nugetter
|
public class Nugetter
|
||||||
{
|
{
|
||||||
// this object can figure out the right framework folders to use from a set of packages
|
// this object can figure out the right framework folders to use from a set of packages
|
||||||
|
|
@ -82,15 +81,13 @@ namespace TShockPluginManager
|
||||||
// make sure the source repository can actually tell us about dependencies
|
// make sure the source repository can actually tell us about dependencies
|
||||||
var dependencyInfoResource = await sourceRepository.GetResourceAsync<DependencyInfoResource>();
|
var dependencyInfoResource = await sourceRepository.GetResourceAsync<DependencyInfoResource>();
|
||||||
// get the try and dependencies
|
// get the try and dependencies
|
||||||
// (the above function returns a nullable value, but doesn't properly indicate it as such)
|
if (dependencyInfoResource is null) continue;
|
||||||
#pragma warning disable CS8602
|
var dependencyInfo = await dependencyInfoResource.ResolvePackage(
|
||||||
var dependencyInfo = await dependencyInfoResource?.ResolvePackage(
|
|
||||||
package, framework, cacheContext, logger, CancellationToken.None);
|
package, framework, cacheContext, logger, CancellationToken.None);
|
||||||
#pragma warning restore CS8602
|
|
||||||
|
|
||||||
// oop, we don't have the ability to get dependency info from this repository, or
|
// oop, we don't have the ability to get dependency info from this repository, or
|
||||||
// it wasn't found. let's try the next source repository!
|
// it wasn't found. let's try the next source repository!
|
||||||
if (dependencyInfo == null) continue;
|
if (dependencyInfo is null) continue;
|
||||||
|
|
||||||
availablePackages.Add(dependencyInfo);
|
availablePackages.Add(dependencyInfo);
|
||||||
foreach (var dependency in dependencyInfo.Dependencies)
|
foreach (var dependency in dependencyInfo.Dependencies)
|
||||||
|
|
@ -302,11 +299,14 @@ namespace TShockPluginManager
|
||||||
|
|
||||||
var relativeFolder = Path.GetDirectoryName(packageRelativeFilePath);
|
var relativeFolder = Path.GetDirectoryName(packageRelativeFilePath);
|
||||||
var targetFolder = Path.Join(isPlugin ? "./ServerPlugins" : "./bin", relativeFolder);
|
var targetFolder = Path.Join(isPlugin ? "./ServerPlugins" : "./bin", relativeFolder);
|
||||||
|
if (File.Exists(filePath))
|
||||||
|
{
|
||||||
Directory.CreateDirectory(targetFolder);
|
Directory.CreateDirectory(targetFolder);
|
||||||
File.Copy(filePath, Path.Join(targetFolder, Path.GetFileName(filePath)), true);
|
File.Copy(filePath, Path.Join(targetFolder, Path.GetFileName(filePath)), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>downloads and installs the given packages</summary>
|
/// <summary>downloads and installs the given packages</summary>
|
||||||
public async Task DownloadAndInstall(PackageIdentity[] userRequest)
|
public async Task DownloadAndInstall(PackageIdentity[] userRequest)
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NuGet.Packaging" Version="6.3.1" />
|
<PackageReference Include="NuGet.Packaging" Version="6.3.4" />
|
||||||
<PackageReference Include="NuGet.Protocol" Version="6.3.1" />
|
<PackageReference Include="NuGet.Protocol" Version="6.3.3" />
|
||||||
<PackageReference Include="NuGet.Resolver" Version="6.3.1" />
|
<PackageReference Include="NuGet.Resolver" Version="6.3.1" />
|
||||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="6.0.0" />
|
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="6.0.0" />
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8a3fffd71db401736ea80619122c70c449c10ff3
|
Subproject commit d4bb7e3a21e875cfeb23bcf5cf847c85d9470ccf
|
||||||
|
|
@ -78,6 +78,33 @@ Use past tense when adding new entries; sign your name off when you add or chang
|
||||||
* If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. -->
|
* If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. -->
|
||||||
|
|
||||||
## Upcoming changes
|
## Upcoming changes
|
||||||
|
* Added a variable for handshake (True upon spawn player), clients no longer notify others of their presence and cant chat if this is never set to true. (@ohayo)
|
||||||
|
* Fixed a security issue with how bans are handled on join. (@ohayo)
|
||||||
|
* Fixed `/dump-reference-data` mutate the command names. (#2943, @sgkoishi)
|
||||||
|
* Added `ParryDamageBuff` (Striking Moment with Brand of the Inferno and shield) for player, updated `CursedInferno` buff for NPC (@sgkoishi, #3005)
|
||||||
|
* Changed the use of `Player.active` to `TSPlayer.Active` for consistency. (@sgkoishi, #2939)
|
||||||
|
* Fix typo in config for IP bans. (@redchess64)
|
||||||
|
* Updated `TShockAPI.NetItem` (@AgaSpace):
|
||||||
|
* Added constructor overload with parameter `Terraria.Item`.
|
||||||
|
* Added the `ToItem` method to get a copy of `Terraria.Item`.
|
||||||
|
* In the constructor `stack` and `prefix` are now optional parameters.
|
||||||
|
* Fixed unable to transfer long response body for REST API. (@sgkoishi, #2925)
|
||||||
|
* Fixed the `/wind` command not being very helpful. (@punchready)
|
||||||
|
* Fixed /help, /me, and /p commands can't work in non-English languages. (@ACaiCat)
|
||||||
|
* Added a hook `AccountHooks.AccountGroupUpdate`, which is called when you change the user group. (@AgaSpace)
|
||||||
|
* * Ensured `TSPlayer.PlayerData` is non-null whilst syncing loadouts. (@drunderscore)
|
||||||
|
* * Detected invalid installations, by checking for a file named `TerrariaServer.exe`. (@drunderscore)
|
||||||
|
* This made the two most common installation mistakes (extracting into the Terraria client directory, and extracting TShock 5 or newer into a TShock 4 or older install) prompt the user with a more useful diagnostic, rather than (likely) crashing moments later. Rewrote bed spawning for SSC. (@PotatoCider)
|
||||||
|
* Removed `TSPlayer.s{X,Y}` in favour of using desyncing client and server spawnpoint values (`Terraria.Player.Spawn{X,Y}`) until the player has changed their spawnpoint per session.
|
||||||
|
* Partially fixed the bed spawning bug when SSC is enabled. Players would need to spawn at their beds at least once to tell TShock that the player's spawnpoint has changed.
|
||||||
|
* Changed Bouncer to block updates which set the following fields to infinity or NaN: player position, projectile position, projectile velocity, item position, and item velocity. (@Arthri)
|
||||||
|
* Updated `TShockAPI.Handlers.SendTileRectHandler` (@LaoSparrow):
|
||||||
|
* Fixed incorrect validating range in `TileRectMatch.MatchRemoval`.
|
||||||
|
* Fixed tile rect changes (e.g. turning on and off campfires) are not synced between clients.
|
||||||
|
* Fixed unable to place Hat Rack without permission `tshock.ignore.sendtilesquare`.
|
||||||
|
* Updated `GetDataHandlers` to ignore `NpcItemStrike(msgid 24)`, which should never be sent by a vanilla client. (@LaoSparrow)
|
||||||
|
|
||||||
|
## TShock 5.2.1
|
||||||
* Updated `TSPlayer.GodMode`. (@AgaSpace)
|
* Updated `TSPlayer.GodMode`. (@AgaSpace)
|
||||||
* Previously the field was used as some kind of dataset changed by /godmode command, but now it is a property that receives/changes data in journey mode.
|
* Previously the field was used as some kind of dataset changed by /godmode command, but now it is a property that receives/changes data in journey mode.
|
||||||
* Added the `TSPlayer.Client` property. It allows the developer to get the `RemoteClient` player, without an additional call to `Terraria.Netplay.Clients`. (@AgaSpace)
|
* Added the `TSPlayer.Client` property. It allows the developer to get the `RemoteClient` player, without an additional call to `Terraria.Netplay.Clients`. (@AgaSpace)
|
||||||
|
|
@ -90,12 +117,16 @@ Use past tense when adding new entries; sign your name off when you add or chang
|
||||||
* Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace)
|
* Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace)
|
||||||
* Fixed bug where when the `UseSqlLogs` config property is true, an empty log file would still get created. (@ZakFahey)
|
* Fixed bug where when the `UseSqlLogs` config property is true, an empty log file would still get created. (@ZakFahey)
|
||||||
* Fixed typo in `/gbuff`. (@sgkoishi, #2955)
|
* Fixed typo in `/gbuff`. (@sgkoishi, #2955)
|
||||||
* Rewrote the `.dockerignore` file into a denylist. (@timschumi)
|
|
||||||
* Added CI for Docker images. (@timschumi)
|
Rewrote the Added a new permission, `tshock.world.time.usemoondial`, for regulating use of Enchanted Moondial. (@Arthri)
|
||||||
* Added a new permission, `tshock.world.time.usemoondial`, for regulating use of Enchanted Moondial. (@Arthri)
|
|
||||||
* Added a set of new permissions, `tshock.specialeffects.{type}`, for regulating use of new special effects(Packet 51) which are not yet recognized by TShock. (@Arthri)
|
* Added a set of new permissions, `tshock.specialeffects.{type}`, for regulating use of new special effects(Packet 51) which are not yet recognized by TShock. (@Arthri)
|
||||||
* Added check for `tshock.npc.summonboss` permission for Skeletron summoning. (@Arthri)
|
* Added check for `tshock.npc.summonboss` permission for Skeletron summoning. (@Arthri)
|
||||||
* Fixed `DisableDungeonGuardian` disabling Skeletron summon instead. The config option is useless as of writing. (@Arthri)
|
* Fixed `DisableDungeonGuardian` disabling Skeletron summon instead. The config option is useless as of writing. (@Arthri)
|
||||||
|
* Added a constructor for `TShockAPI.PlayerData` that accepts the `includingStarterInventory` parameter, which is responsible for loading the TShock inventory.
|
||||||
|
* Declared the constructor `TShockAPI.PlayerData` accepting the argument `TShockAPI.TSPlayer` obsolete.
|
||||||
|
* Updated the `PlayerData.StoreSlot` method: Added an overload that takes `TShockAPI.NetItem`.
|
||||||
|
* Added `PlayerHooks.PrePlayerCommand` hook, which fired before command execution. (@AgaSpace)
|
||||||
|
* Added `PlayerHooks.PostPlayerCommand` hook, which fired after command execution. (@AgaSpace)
|
||||||
|
|
||||||
## TShock 5.2
|
## TShock 5.2
|
||||||
* An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK)
|
* An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK)
|
||||||
|
|
@ -129,7 +160,6 @@ Use past tense when adding new entries; sign your name off when you add or chang
|
||||||
* Relaxed custom death message restrictions to allow Inferno potions in PvP. (@drunderscore)
|
* Relaxed custom death message restrictions to allow Inferno potions in PvP. (@drunderscore)
|
||||||
* Allowed Flower Boots to place Ash Flowers on Ash Grass blocks. (@punchready)
|
* Allowed Flower Boots to place Ash Flowers on Ash Grass blocks. (@punchready)
|
||||||
* Removed unnecessary range check that artifically shortened quick stack reach. (@boddyn, #2885, @bcat)
|
* Removed unnecessary range check that artifically shortened quick stack reach. (@boddyn, #2885, @bcat)
|
||||||
* Re-wrote tile rect handling from scratch, fixing a certain exploitable flaw in the old code and significantly reducing the potential exploit surface, potentially even down to zero. (@punchready)
|
|
||||||
|
|
||||||
## TShock 5.1.3
|
## TShock 5.1.3
|
||||||
* Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef)
|
* Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef)
|
||||||
|
|
|
||||||
|
|
@ -14,32 +14,27 @@ Open ports can also be passed through using `-p <host_port>:<container_port>`.
|
||||||
|
|
||||||
For Example:
|
For Example:
|
||||||
```bash
|
```bash
|
||||||
# Building the image using buildx and loading it into docker
|
|
||||||
docker buildx build -t tshock:latest --load .
|
|
||||||
|
|
||||||
# Running the image
|
|
||||||
docker run -p 7777:7777 -p 7878:7878 \
|
docker run -p 7777:7777 -p 7878:7878 \
|
||||||
-v /home/cider/tshock/:/tshock \
|
-v /home/cider/tshock/:/tshock \
|
||||||
-v /home/cider/.local/share/Terraria/Worlds:/worlds \
|
-v /home/cider/.local/share/Terraria/Worlds:/worlds \
|
||||||
-v /home/cider/tshock/plugins:/plugins \
|
-v /home/cider/tshock/plugins:/plugins \
|
||||||
--rm -it tshock:latest \
|
--rm -it ghcr.io/pryaxis/tshock:latest \
|
||||||
-world /worlds/backflip.wld -motd "OMFG DOCKER"
|
-world /worlds/backflip.wld -motd "OMFG DOCKER"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building for Other Platforms
|
## Building custom images
|
||||||
|
|
||||||
Using `docker buildx`, you could build [multi-platform images](https://docs.docker.com/build/building/multi-platform/) for TShock.
|
Occasionally, it may be necessary to adjust TShock with customizations that are not included in the upstream project.
|
||||||
|
Therefore, these changes are also not available in the officially provided Docker images.
|
||||||
|
|
||||||
|
To build and load a Docker image from your local checkout, use the following `buildx` command:
|
||||||
|
|
||||||
For Example:
|
|
||||||
```bash
|
```bash
|
||||||
# Building the image using buildx and loading it into docker
|
docker buildx build -t tshock:latest --load .
|
||||||
docker buildx build -t tshock:linux-arm64 --platform linux/arm64 --load .
|
```
|
||||||
|
|
||||||
# Running the image
|
It is also possible to build [multi-platform images](https://docs.docker.com/build/building/multi-platform/) for TShock (e.g. an image targeting `arm64`, on a host that is not `arm64`):
|
||||||
docker run -p 7777:7777 -p 7878:7878 \
|
|
||||||
-v /home/cider/tshock/:/tshock \
|
```bash
|
||||||
-v /home/cider/.local/share/Terraria/Worlds:/worlds \
|
docker buildx build -t tshock:linux-arm64 --platform linux/arm64 --load .
|
||||||
-v /home/cider/tshock/plugins:/plugins \
|
|
||||||
--rm -it tshock:linux-arm64 \
|
|
||||||
-world /worlds/backflip.wld -motd "ARM64 ftw"
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,7 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script>
|
<script>
|
||||||
window.$docsify = {
|
window.location.replace("https://github.com/Pryaxis/TShock/wiki");
|
||||||
name: 'TShock for Terraria',
|
|
||||||
repo: 'pryaxis/tshock',
|
|
||||||
loadSidebar: true,
|
|
||||||
subMaxLevel: 9
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<!-- Docsify v4 -->
|
<!-- Docsify v4 -->
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": [
|
|
||||||
"config:base"
|
|
||||||
],
|
|
||||||
"git-submodules": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"matchPackageNames": ["OTAPI.Upcoming", "ModFramework", "TerrariaServerAPI"],
|
|
||||||
"ignoreUnstable": "false",
|
|
||||||
"bumpVersion": "prerelease",
|
|
||||||
"groupName": "OTAPI things"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue