diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs
index 4a126d567..5c75eb6b3 100644
--- a/TShockAPI/GetDataHandlers.cs
+++ b/TShockAPI/GetDataHandlers.cs
@@ -265,6 +265,25 @@ private static bool OnPlayerSlot(TSPlayer player, MemoryStream data, byte _plr,
return args.Handled;
}
+ ///
+ /// Connecting - called at a Connecting event
+ ///
+ public static HandlerList Connecting = new HandlerList();
+ private static bool OnConnecting(TSPlayer player, MemoryStream data)
+ {
+ if (Connecting == null)
+ return false;
+
+ var args = new GetDataHandledEventArgs
+ {
+ Player = player,
+
+ Data = data
+ };
+ Connecting.Invoke(null, args);
+ return args.Handled;
+ }
+
/// The arguments to a GetSection packet.
public class GetSectionEventArgs : GetDataHandledEventArgs
{
@@ -936,6 +955,35 @@ private static bool OnPlayerZone(TSPlayer player, MemoryStream data, byte plr, B
PlayerZone.Invoke(null, args);
return args.Handled;
}
+
+ ///
+ /// For use in a Password Event
+ ///
+ public class PasswordEventArgs : GetDataHandledEventArgs
+ {
+ ///
+ /// The Password
+ ///
+ public string Password { get; set; }
+ }
+ ///
+ /// Password - When clients send password to server
+ ///
+ public static HandlerList Password = new HandlerList();
+ private static bool OnPassword(TSPlayer player, MemoryStream data, string password)
+ {
+ if (Password == null)
+ return false;
+
+ var args = new PasswordEventArgs
+ {
+ Player = player,
+ Data = data,
+ Password = password
+ };
+ Password.Invoke(null, args);
+ return args.Handled;
+ }
///
/// For use with a PlayerAnimation event
@@ -1942,29 +1990,24 @@ private static bool HandleConnecting(GetDataHandlerArgs args)
args.Player.tempGroup = null;
args.Player.Account = account;
args.Player.IsLoggedIn = true;
- args.Player.IsDisabledForSSC = false;
- if (Main.ServerSideCharacter)
+ if (OnConnecting(args.Player, args.Data))
{
- if (args.Player.HasPermission(Permissions.bypassssc))
- {
- args.Player.PlayerData.CopyCharacter(args.Player);
- TShock.CharacterDB.InsertPlayerData(args.Player);
- }
- args.Player.PlayerData.RestoreCharacter(args.Player);
+ return true;
}
- args.Player.LoginFailsBySsi = false;
-
- if (args.Player.HasPermission(Permissions.ignorestackhackdetection))
- args.Player.IsDisabledForStackDetection = false;
+ else
+ {
+ if (args.Player.HasPermission(Permissions.ignorestackhackdetection))
+ args.Player.IsDisabledForStackDetection = false;
- if (args.Player.HasPermission(Permissions.usebanneditem))
- args.Player.IsDisabledForBannedWearable = false;
+ if (args.Player.HasPermission(Permissions.usebanneditem))
+ args.Player.IsDisabledForBannedWearable = false;
- args.Player.SendSuccessMessage("Authenticated as " + account.Name + " successfully.");
- TShock.Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user " + args.Player.Name + ".");
- Hooks.PlayerHooks.OnPlayerPostLogin(args.Player);
- return true;
+ args.Player.SendSuccessMessage("Authenticated as " + account.Name + " successfully.");
+ TShock.Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user " + args.Player.Name + ".");
+ Hooks.PlayerHooks.OnPlayerPostLogin(args.Player);
+ return true;
+ }
}
}
else if (account != null && !TShock.Config.DisableLoginBeforeJoin)
@@ -2010,23 +2053,7 @@ private static bool HandleSpawn(GetDataHandlerArgs args)
if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny))
return true;
-
- 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)))
- {
-
- args.Player.sX = args.TPlayer.SpawnX;
- args.Player.sY = args.TPlayer.SpawnY;
-
- if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == 79)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1)))
- args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48);
- }
-
- else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0))
- {
- if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == 79)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1)))
- args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48);
- }
-
+
args.Player.Dead = false;
return false;
}
@@ -2138,36 +2165,7 @@ private static bool HandlePlayerUpdate(GetDataHandlerArgs args)
{
args.TPlayer.direction = -1;
}
-
- if (args.Player.Confused && Main.ServerSideCharacter && args.Player.IsLoggedIn)
- {
- if (args.TPlayer.controlUp)
- {
- args.TPlayer.controlDown = true;
- args.TPlayer.controlUp = false;
- }
- else if (args.TPlayer.controlDown)
- {
- args.TPlayer.controlDown = false;
- args.TPlayer.controlUp = true;
- }
-
- if (args.TPlayer.controlLeft)
- {
- args.TPlayer.controlRight = true;
- args.TPlayer.controlLeft = false;
- }
- else if (args.TPlayer.controlRight)
- {
- args.TPlayer.controlRight = false;
- args.TPlayer.controlLeft = true;
- }
-
- args.TPlayer.Update(args.TPlayer.whoAmI);
- NetMessage.SendData((int)PacketTypes.PlayerUpdate, -1, -1, NetworkText.Empty, args.Player.Index);
- return true;
- }
-
+
NetMessage.SendData((int)PacketTypes.PlayerUpdate, -1, args.Player.Index, NetworkText.Empty, args.Player.Index);
return true;
}
@@ -2513,31 +2511,25 @@ private static bool HandlePassword(GetDataHandlerArgs args)
args.Player.tempGroup = null;
args.Player.Account = account;
args.Player.IsLoggedIn = true;
- args.Player.IsDisabledForSSC = false;
- if (Main.ServerSideCharacter)
+ if (OnPassword(args.Player, args.Data, password))
{
- if (args.Player.HasPermission(Permissions.bypassssc))
- {
- args.Player.PlayerData.CopyCharacter(args.Player);
- TShock.CharacterDB.InsertPlayerData(args.Player);
- }
- args.Player.PlayerData.RestoreCharacter(args.Player);
+ return true;
+ }
+ else
+ {
+ if (args.Player.HasPermission(Permissions.ignorestackhackdetection))
+ args.Player.IsDisabledForStackDetection = false;
+
+ if (args.Player.HasPermission(Permissions.usebanneditem))
+ args.Player.IsDisabledForBannedWearable = false;
+
+ args.Player.SendMessage("Authenticated as " + args.Player.Name + " successfully.", Color.LimeGreen);
+ TShock.Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user " + args.Player.Name + ".");
+ TShock.UserAccounts.SetUserAccountUUID(account, args.Player.UUID);
+ Hooks.PlayerHooks.OnPlayerPostLogin(args.Player);
+ return true;
}
- args.Player.LoginFailsBySsi = false;
-
- if (args.Player.HasPermission(Permissions.ignorestackhackdetection))
- args.Player.IsDisabledForStackDetection = false;
-
- if (args.Player.HasPermission(Permissions.usebanneditem))
- args.Player.IsDisabledForBannedWearable = false;
-
-
- args.Player.SendMessage("Authenticated as " + args.Player.Name + " successfully.", Color.LimeGreen);
- TShock.Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user " + args.Player.Name + ".");
- TShock.UserAccounts.SetUserAccountUUID(account, args.Player.UUID);
- Hooks.PlayerHooks.OnPlayerPostLogin(args.Player);
- return true;
}
args.Player.Kick("Your password did not match this character's password.", true, true);
return true;
@@ -3319,15 +3311,6 @@ private static bool HandlePlayerKillMeV2(GetDataHandlerArgs args)
}
}
- if (args.TPlayer.difficulty == 2 && Main.ServerSideCharacter && args.Player.IsLoggedIn)
- {
- if (TShock.CharacterDB.RemovePlayer(args.Player.Account.ID))
- {
- args.Player.SendErrorMessage("You have fallen in hardcore mode, and your items have been lost forever.");
- TShock.CharacterDB.SeedInitialData(args.Player.Account);
- }
- }
-
return false;
}
diff --git a/TShockAPI/SSC.cs b/TShockAPI/SSC.cs
new file mode 100644
index 000000000..e55d57ed1
--- /dev/null
+++ b/TShockAPI/SSC.cs
@@ -0,0 +1,207 @@
+/*
+TShock, a server mod for Terraria
+Copyright (C) 2011-2018 Pryaxis & TShock Contributors
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using Terraria.ID;
+using TShockAPI.DB;
+using TShockAPI.Net;
+using Terraria;
+using Microsoft.Xna.Framework;
+using OTAPI.Tile;
+using TShockAPI.Localization;
+using static TShockAPI.GetDataHandlers;
+using TerrariaApi.Server;
+using Terraria.ObjectData;
+using Terraria.DataStructures;
+using Terraria.Localization;
+
+namespace TShockAPI
+{
+ /// The TShock server side character subsystem.
+ internal sealed class SSC
+ {
+ /// The database connection layer to for the ssc subsystem.
+ private CharacterManager DataModel;
+ /// A reference to the TShock plugin so we can register events.
+ private TShock Plugin;
+
+ /// A new SSC system.
+ internal SSC(TShock plugin, IDbConnection database)
+ {
+ DataModel = new CharacterManager(database);
+ Plugin = plugin;
+
+ // Setup GetDataHandlers
+ GetDataHandlers.Connecting += Login;
+ GetDataHandlers.PlayerSpawn += OnSpawn;
+ GetDataHandlers.PlayerUpdate += OnPlayerUpdate;
+ GetDataHandlers.Password += Login;
+ GetDataHandlers.KillMe += OnKillMe;
+
+ // Setup ServerApi Hook Handler
+ ServerApi.Hooks.GameUpdate.Register(Plugin, OnGameUpdate);
+ ServerApi.Hooks.NetGreetPlayer.Register(Plugin, OnGreetPlayer);
+ ServerApi.Hooks.ServerLeave.Register(Plugin, OnLeave);
+ }
+
+ internal void Login(object sender, GetDataHandledEventArgs args)
+ {
+ try
+ {
+ args.Player.IsDisabledForSSC = false;
+
+ if (args.Player.HasPermission(Permissions.bypassssc))
+ {
+ args.Player.PlayerData.CopyCharacter(args.Player);
+ DataModel.InsertPlayerData(args.Player);
+ }
+
+ args.Player.PlayerData.RestoreCharacter(args.Player);
+ args.Player.LoginFailsBySsi = false;
+ }
+ catch (Exception ex)
+ {
+ TShock.Log.ConsoleError(ex.ToString());
+
+ args.Player.IsDisabledForSSC = true;
+ args.Player.LoginFailsBySsi = true;
+ }
+ }
+
+ internal void OnSpawn(object sender, SpawnEventArgs args)
+ {
+ var p = args.Player;
+ if ((p.sX > 0) && (p.sY > 0) && (p.TPlayer.SpawnX > 0) &&
+ ((p.TPlayer.SpawnX != p.sX) && (p.TPlayer.SpawnY != p.sY)))
+ {
+
+ p.sX = p.TPlayer.SpawnX;
+ p.sY = p.TPlayer.SpawnY;
+
+ if (((Main.tile[p.sX, p.sY - 1].active() && Main.tile[p.sX, p.sY - 1].type == 79)) &&
+ (WorldGen.StartRoomCheck(p.sX, p.sY - 1)))
+ p.Teleport(p.sX * 16, (p.sY * 16) - 48);
+ }
+
+ else if ((p.sX > 0) && (p.sY > 0))
+ {
+ if (((Main.tile[p.sX, p.sY - 1].active() && Main.tile[p.sX, p.sY - 1].type == 79)) &&
+ (WorldGen.StartRoomCheck(p.sX, p.sY - 1)))
+ p.Teleport(p.sX * 16, (p.sY * 16) - 48);
+ }
+ }
+
+ internal void OnPlayerUpdate(object sender, PlayerUpdateEventArgs args)
+ {
+ var tplr = args.Player.TPlayer;
+ if (args.Player.Confused && args.Player.IsLoggedIn)
+ {
+ if (tplr.controlUp)
+ {
+ tplr.controlDown = true;
+ tplr.controlUp = false;
+ }
+ else if (tplr.controlDown)
+ {
+ tplr.controlDown = false;
+ tplr.controlUp = true;
+ }
+
+ if (tplr.controlLeft)
+ {
+ tplr.controlRight = true;
+ tplr.controlLeft = false;
+ }
+ else if (tplr.controlRight)
+ {
+ tplr.controlRight = false;
+ tplr.controlLeft = true;
+ }
+
+ tplr.Update(tplr.whoAmI);
+ NetMessage.SendData((int) PacketTypes.PlayerUpdate, -1, -1, NetworkText.Empty, args.Player.Index);
+ args.Handled = true;
+ }
+ }
+
+ internal void OnKillMe(object sender, KillMeEventArgs args)
+ {
+ if (args.Player.TPlayer.difficulty == 2 && args.Player.IsLoggedIn)
+ {
+ if (DataModel.RemovePlayer(args.Player.Account.ID))
+ {
+ args.Player.SendErrorMessage(
+ "You have fallen in hardcore mode, and your items have been lost forever.");
+ TShock.CharacterDB.SeedInitialData(args.Player.Account);
+ }
+ }
+ }
+
+ internal void OnGameUpdate(EventArgs args)
+ {
+ if ((DateTime.UtcNow - Plugin.LastSave).TotalMinutes >=
+ TShock.ServerSideCharacterConfig.ServerSideCharacterSave)
+ {
+ foreach (TSPlayer player in TShock.Players)
+ {
+ // prevent null point exceptions
+ if (player != null && player.IsLoggedIn && !player.IsDisabledPendingTrashRemoval)
+ {
+
+ DataModel.InsertPlayerData(player);
+ }
+ }
+
+ Plugin.LastSave = DateTime.UtcNow;
+ }
+ }
+
+ internal void OnGreetPlayer(GreetPlayerEventArgs args)
+ {
+ var player = TShock.Players[args.Who];
+
+ if (!player.IsLoggedIn)
+ {
+ player.IsDisabledForSSC = true;
+ player.SendErrorMessage(String.Format(
+ "Server side characters is enabled! Please {0}register or {0}login to play!", Commands.Specifier));
+ player.LoginHarassed = true;
+ }
+ }
+
+ private void OnLeave(LeaveEventArgs args)
+ {
+ var tsplr = TShock.Players[args.Who];
+ if (tsplr == null)
+ {
+ return;
+ }
+
+ if (tsplr.ReceivedInfo)
+ {
+ if (tsplr.IsLoggedIn && !tsplr.IsDisabledPendingTrashRemoval && (!tsplr.Dead || tsplr.TPlayer.difficulty != 2))
+ {
+ tsplr.PlayerData.CopyCharacter(tsplr);
+ DataModel.InsertPlayerData(tsplr);
+ }
+ }
+ }
+ }
+}
diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs
index f839019d6..370ff49c6 100644
--- a/TShockAPI/TShock.cs
+++ b/TShockAPI/TShock.cs
@@ -139,6 +139,11 @@ public class TShock : TerrariaPlugin
///
internal RegionHandler RegionSystem;
+ ///
+ /// TShock's SSC subsystem.
+ ///
+ internal SSC SSC;
+
///
/// Called after TShock is initialized. Useful for plugins that needs hooks before tshock but also depend on tshock being loaded.
///
@@ -325,6 +330,8 @@ public override void Initialize()
RestManager.RegisterRestfulCommands();
Bouncer = new Bouncer();
RegionSystem = new RegionHandler(Regions);
+ if(Main.ServerSideCharacter)
+ SSC = new SSC(this, DB);
var geoippath = "GeoIP.dat";
if (Config.EnableGeoIP && File.Exists(geoippath))
@@ -897,10 +904,10 @@ private void OnPostInit(EventArgs args)
}
/// LastCheck - Used to keep track of the last check for basically all time based checks.
- private DateTime LastCheck = DateTime.UtcNow;
+ internal DateTime LastCheck = DateTime.UtcNow;
/// LastSave - Used to keep track of SSC save intervals.
- private DateTime LastSave = DateTime.UtcNow;
+ internal DateTime LastSave = DateTime.UtcNow;
/// OnUpdate - Called when ever the server ticks.
/// args - EventArgs args
@@ -914,20 +921,6 @@ private void OnUpdate(EventArgs args)
OnSecondUpdate();
LastCheck = DateTime.UtcNow;
}
-
- if (Main.ServerSideCharacter && (DateTime.UtcNow - LastSave).TotalMinutes >= ServerSideCharacterConfig.ServerSideCharacterSave)
- {
- foreach (TSPlayer player in Players)
- {
- // prevent null point exceptions
- if (player != null && player.IsLoggedIn && !player.IsDisabledPendingTrashRemoval)
- {
-
- CharacterDB.InsertPlayerData(player);
- }
- }
- LastSave = DateTime.UtcNow;
- }
}
/// OnSecondUpdate - Called effectively every second for all time based checks.
@@ -1634,13 +1627,7 @@ private void OnGreetPlayer(GreetPlayerEventArgs args)
if (!player.IsLoggedIn)
{
- if (Main.ServerSideCharacter)
- {
- player.IsDisabledForSSC = true;
- player.SendErrorMessage(String.Format("Server side characters is enabled! Please {0}register or {0}login to play!", Commands.Specifier));
- player.LoginHarassed = true;
- }
- else if (Config.RequireLogin)
+ if (Main.ServerSideCharacter && Config.RequireLogin)
{
player.SendErrorMessage("Please {0}register or {0}login to play!", Commands.Specifier);
player.LoginHarassed = true;
diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj
index b3deaaeaf..a76a2aeda 100644
--- a/TShockAPI/TShockAPI.csproj
+++ b/TShockAPI/TShockAPI.csproj
@@ -97,6 +97,7 @@
+