Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.86")]
[assembly: AssemblyFileVersion("0.1.86")]
[assembly: AssemblyVersion("0.1.87")]
[assembly: AssemblyFileVersion("0.1.87")]
2 changes: 1 addition & 1 deletion Source/Core/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace PraetorisClient
public class PraetorisClientPlugin : BaseUnityPlugin
{
private const string ModName = "PraetorisClient";
private const string ModVersion = "0.1.86";
private const string ModVersion = "0.1.87";
private const string Author = "warpalicious";
private const string ModGUID = Author + "." + ModName;
private const string EpicLootGuid = "randyknapp.mods.epicloot";
Expand Down
6 changes: 4 additions & 2 deletions Source/Network/RpcNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ internal static class RpcNames
public const string ServerChestCommandRequest = "PraetorisClient_ServerChest_CommandRequest";
public const string ServerChestCommandResponse = "PraetorisClient_ServerChest_CommandResponse";
public const string ShipPasswordSetRequest = "PraetorisClient_ShipPassword_SetRequest";
public const string ShipPasswordSetGrant = "PraetorisClient_ShipPassword_SetGrant";
public const string ShipPasswordSetGrant = "PraetorisClient_ShipPassword_SetGrantV2";
public const string ShipPasswordControlRequest = "PraetorisClient_ShipPassword_ControlRequest";
public const string ShipPasswordControlGrant = "PraetorisClient_ShipPassword_ControlGrant";
public const string ShipPasswordControlGrant = "PraetorisClient_ShipPassword_ControlGrantV2";
public const string ShipPasswordStorageRequest = "PraetorisClient_ShipPassword_StorageRequest";
public const string ShipPasswordStorageGrant = "PraetorisClient_ShipPassword_StorageGrant";
public const string ShipPasswordOwnerResponse = "PraetorisClient_ShipPassword_OwnerResponse";
public const string ShipPasswordResponse = "PraetorisClient_ShipPassword_Response";
public const string MaintenanceNotice = "PraetorisClient_MaintenanceNotice";
Expand Down
23 changes: 23 additions & 0 deletions Source/ShipPassword/ShipPasswordData.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Security.Cryptography;

namespace PraetorisClient.ShipPasswordFeature
Expand All @@ -12,6 +13,28 @@ internal static class ShipPasswordData

internal static readonly int SaltHash = "PraetorisShipPassword_salt".GetStableHashCode();
internal static readonly int VerifierHash = "PraetorisShipPassword_verifier".GetStableHashCode();
private static readonly int AuthorizedPlayersHash = "PraetorisShipPassword_players".GetStableHashCode();

internal static bool HasAccess(ZDO? zdo, long playerId)
{
if (zdo == null || playerId == 0L)
{
return false;
}

string entry = "|" + playerId.ToString(CultureInfo.InvariantCulture) + "|";
return zdo.GetString(AuthorizedPlayersHash, "").Contains(entry);
}

internal static void GrantAccess(ZDO zdo, long playerId)
{
if (playerId != 0L && !HasAccess(zdo, playerId))
{
// The boat owns this record. Password changes preserve it; destruction removes it.
zdo.Set(AuthorizedPlayersHash, zdo.GetString(AuthorizedPlayersHash, "") +
"|" + playerId.ToString(CultureInfo.InvariantCulture) + "|");
}
}

internal static bool IsProtected(ZDO? zdo)
{
Expand Down
71 changes: 68 additions & 3 deletions Source/ShipPassword/ShipPasswordInteraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ private enum InputMode
{
None,
SetPassword,
EnterPassword
EnterPassword,
EnterStoragePassword
}

private ShipControlls _controls = null!;
private InputMode _mode;
private Container? _container;
private float _storageRetryUntil;

private void Awake()
{
Expand Down Expand Up @@ -45,9 +48,57 @@ internal bool BeginEnterPassword(Player player)
return false;
}

ZNetView nview = _controls.m_ship.GetComponent<ZNetView>();
if (nview != null && nview.IsValid() && ShipPasswordData.HasAccess(nview.GetZDO(), player.GetPlayerID()))
{
ShipPasswordRpc.RequestControl(nview.GetZDO().m_uid, "");
return false;
}

return Open(InputMode.EnterPassword, "Enter ship password");
}

internal bool BeginStoragePassword(Player player, Container container)
{
if (player != Player.m_localPlayer || !InStorageRange(player, container))
{
return false;
}

_container = container;
_storageRetryUntil = 0f;
return Open(InputMode.EnterStoragePassword, "Enter ship password");
}

private void Update()
{
if (_storageRetryUntil == 0f)
{
return;
}

Player player = Player.m_localPlayer;
if (Time.realtimeSinceStartup > _storageRetryUntil || _container == null || player == null ||
!InStorageRange(player, _container))
{
_storageRetryUntil = 0f;
return;
}

ZDO? zdo = ShipPasswordStorage.GetShipZdo(_container!);
if (ShipPasswordData.HasAccess(zdo, player.GetPlayerID()))
{
_storageRetryUntil = 0f;
_container.Interact(player, false, false);
}
}

private static bool InStorageRange(Player player, Container container)
{
return Vector3.Distance(player.transform.position, container.transform.position) <=
player.m_maxInteractDistance + container.m_hoverOffset;
}

public string GetText()
{
return "";
Expand All @@ -70,15 +121,29 @@ public void SetText(string text)
{
ShipPasswordRpc.RequestSetPassword(nview.GetZDO().m_uid, password);
}
else if (mode == InputMode.EnterPassword)
else if (mode == InputMode.EnterPassword || mode == InputMode.EnterStoragePassword)
{
if (password.Length == 0)
{
ShowMessage("Enter the ship password.");
return;
}

ShipPasswordRpc.RequestControl(nview.GetZDO().m_uid, password);
if (mode == InputMode.EnterStoragePassword)
{
if (_container == null || Player.m_localPlayer == null ||
!InStorageRange(Player.m_localPlayer, _container))
{
return;
}

_storageRetryUntil = Time.realtimeSinceStartup + 10f;
ShipPasswordRpc.RequestStorageAccess(nview.GetZDO().m_uid, password);
}
else
{
ShipPasswordRpc.RequestControl(nview.GetZDO().m_uid, password);
}
}
}

Expand Down
55 changes: 48 additions & 7 deletions Source/ShipPassword/ShipPasswordRpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ internal static void Register(ZRoutedRpc rpc)
rpc.Register<ZPackage>(RpcNames.ShipPasswordSetGrant, OnSetGrant);
rpc.Register<ZPackage>(RpcNames.ShipPasswordControlRequest, OnControlRequest);
rpc.Register<ZPackage>(RpcNames.ShipPasswordControlGrant, OnControlGrant);
rpc.Register<ZPackage>(RpcNames.ShipPasswordStorageRequest,
(sender, package) => OnAccessRequest(sender, package, false));
rpc.Register<ZPackage>(RpcNames.ShipPasswordStorageGrant,
(sender, package) => OnAccessGrant(sender, package, false));
rpc.Register<ZPackage>(RpcNames.ShipPasswordOwnerResponse, OnOwnerResponse);
rpc.Register<ZPackage>(RpcNames.ShipPasswordResponse, OnResponse);
}
Expand All @@ -33,6 +37,11 @@ internal static void RequestControl(ZDOID shipId, string password)
SendRequest(RpcNames.ShipPasswordControlRequest, shipId, password);
}

internal static void RequestStorageAccess(ZDOID shipId, string password)
{
SendRequest(RpcNames.ShipPasswordStorageRequest, shipId, password);
}

private static void SendRequest(string rpcName, ZDOID shipId, string password)
{
if (ZRoutedRpc.instance == null)
Expand Down Expand Up @@ -65,7 +74,7 @@ private static void OnSetRequest(long sender, ZPackage package)
return;
}

if (!TryGetAuthorizedShip(sender, shipId, creatorRequired: true, out ZDO? shipZdo, out _))
if (!TryGetAuthorizedShip(sender, shipId, creatorRequired: true, out ZDO? shipZdo, out long playerId))
{
SendResponse(sender, false, "Only the nearby ship creator can change its password.");
return;
Expand All @@ -84,11 +93,17 @@ private static void OnSetRequest(long sender, ZPackage package)
grant.Write(sender);
grant.Write(saltValue);
grant.Write(verifierValue);
grant.Write(playerId);
RegisterPendingOwnerResponse(ownerPeerId, sender, shipId);
ZRoutedRpc.instance.InvokeRoutedRPC(ownerPeerId, RpcNames.ShipPasswordSetGrant, grant);
}

private static void OnControlRequest(long sender, ZPackage package)
{
OnAccessRequest(sender, package, true);
}

private static void OnAccessRequest(long sender, ZPackage package, bool takeControl)
{
if (!IsServer())
{
Expand All @@ -99,7 +114,7 @@ private static void OnControlRequest(long sender, ZPackage package)
string password = package.ReadString();
if (!TryGetAuthorizedShip(sender, shipId, creatorRequired: false, out ZDO? shipZdo, out long playerId))
{
SendResponse(sender, false, "You must be aboard the ship to use its helm.");
SendResponse(sender, false, "You must be near the ship to request access.");
return;
}

Expand All @@ -109,7 +124,9 @@ private static void OnControlRequest(long sender, ZPackage package)
return;
}

if (!ShipPasswordData.IsProtected(shipZdo) || !ShipPasswordData.Verify(shipZdo!, password))
if (password.Length > ShipPasswordData.MaximumPasswordLength ||
!ShipPasswordData.IsProtected(shipZdo) ||
(!ShipPasswordData.HasAccess(shipZdo, playerId) && !ShipPasswordData.Verify(shipZdo!, password)))
{
PraetorisClientPlugin.Log.LogInfo("Rejected ship password for " + shipId + ".");
SendResponse(sender, false, "Incorrect ship password.");
Expand All @@ -122,8 +139,10 @@ private static void OnControlRequest(long sender, ZPackage package)
grant.Write(shipId);
grant.Write(sender);
grant.Write(playerId);
grant.Write(shipZdo!.GetString(ShipPasswordData.VerifierHash, ""));
RegisterPendingOwnerResponse(ownerPeerId, sender, shipId);
ZRoutedRpc.instance.InvokeRoutedRPC(ownerPeerId, RpcNames.ShipPasswordControlGrant, grant);
ZRoutedRpc.instance.InvokeRoutedRPC(ownerPeerId,
takeControl ? RpcNames.ShipPasswordControlGrant : RpcNames.ShipPasswordStorageGrant, grant);
PraetorisClientPlugin.Log.LogInfo("Accepted ship password for " + shipId + ".");
}

Expand All @@ -138,6 +157,7 @@ private static void OnSetGrant(long sender, ZPackage package)
long requestingPeerId = package.ReadLong();
string saltValue = package.ReadString();
string verifierValue = package.ReadString();
long playerId = package.ReadLong();
GameObject? shipObject = ZNetScene.instance != null ? ZNetScene.instance.FindInstance(shipId) : null;
ZNetView? nview = shipObject != null ? shipObject.GetComponent<ZNetView>() : null;
if (nview == null || !nview.IsValid() || !nview.IsOwner())
Expand All @@ -148,6 +168,11 @@ private static void OnSetGrant(long sender, ZPackage package)

ShipPasswordData.ApplyVerifier(nview.GetZDO(), saltValue, verifierValue);
bool passwordSet = verifierValue.Length > 0;
if (passwordSet)
{
ShipPasswordData.GrantAccess(nview.GetZDO(), playerId);
ZDOMan.instance.ForceSendZDO(requestingPeerId, shipId);
}
PraetorisClientPlugin.Log.LogInfo(
(passwordSet ? "Set" : "Cleared") + " ship password for " + shipId + ".");
SendOwnerResponse(
Expand All @@ -158,6 +183,11 @@ private static void OnSetGrant(long sender, ZPackage package)
}

private static void OnControlGrant(long sender, ZPackage package)
{
OnAccessGrant(sender, package, true);
}

private static void OnAccessGrant(long sender, ZPackage package, bool takeControl)
{
if (ZRoutedRpc.instance == null || sender != ZRoutedRpc.instance.GetServerPeerID())
{
Expand All @@ -167,19 +197,30 @@ private static void OnControlGrant(long sender, ZPackage package)
ZDOID shipId = package.ReadZDOID();
long requestingPeerId = package.ReadLong();
long playerId = package.ReadLong();
string verifier = package.ReadString();
GameObject? shipObject = ZNetScene.instance != null ? ZNetScene.instance.FindInstance(shipId) : null;
Ship? ship = shipObject != null ? shipObject.GetComponent<Ship>() : null;
ZNetView? nview = shipObject != null ? shipObject.GetComponent<ZNetView>() : null;
ShipControlls? controls = ship != null ? ship.m_shipControlls : null;

if (ship == null || nview == null || controls == null || !nview.IsOwner() ||
!ShipPasswordData.IsProtected(nview.GetZDO()) || !ship.IsPlayerInBoat(playerId))
if (ship == null || nview == null || !nview.IsValid() || !nview.IsOwner() ||
!ShipPasswordData.IsProtected(nview.GetZDO()) ||
nview.GetZDO().GetString(ShipPasswordData.VerifierHash, "") != verifier ||
(takeControl && (controls == null || !ship.IsPlayerInBoat(playerId))))
{
SendOwnerResponse(shipId, requestingPeerId, false, "Ship control request expired. Try again.");
return;
}

if (controls.GetUser() != playerId && controls.HaveValidUser())
ShipPasswordData.GrantAccess(nview.GetZDO(), playerId);
ZDOMan.instance.ForceSendZDO(requestingPeerId, shipId);
if (!takeControl)
{
SendOwnerResponse(shipId, requestingPeerId, true, "Ship access saved. You can use its helm and storage.");
return;
}

if (controls!.GetUser() != playerId && controls.HaveValidUser())
{
SendOwnerResponse(shipId, requestingPeerId, false, "$msg_inuse");
return;
Expand Down
Loading
Loading