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
8 changes: 8 additions & 0 deletions Source/Client/Debug/DebugActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
using LudeonTK;
using Multiplayer.Client.Desyncs;
using Multiplayer.Client.Util;
using Multiplayer.Client.Windows;
using RimWorld;
using RimWorld.Planet;
using Steamworks;
using UnityEngine;
using Verse;
using Debug = UnityEngine.Debug;
Expand Down Expand Up @@ -147,6 +149,12 @@ public static void TriggerDesync()
Multiplayer.game.sync.TryAddStackTraceForDesyncLogRaw(logItem, depth, hash);
}

[DebugAction(MultiplayerCategory, name = "Show pending player", allowedGameStates = AllowedGameStates.Playing)]
public static void ShowPendingPlayer()
{
PendingPlayerWindow.EnqueueJoinRequest(SteamUser.GetSteamID(), (_, _) => { });
}

[DebugAction(MultiplayerCategory, "Dump Sync Types", allowedGameStates = AllowedGameStates.Entry)]
public static void DumpSyncTypes()
{
Expand Down
16 changes: 15 additions & 1 deletion Source/Client/Networking/SteamIntegration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using Multiplayer.Client.Networking;
using Multiplayer.Client.Windows;
using RimWorld;
using Steamworks;
using UnityEngine;
Expand Down Expand Up @@ -33,8 +34,13 @@ public static void InitCallbacks()
if (Multiplayer.settings.autoAcceptSteam)
SteamNetworking.AcceptP2PSessionWithUser(req.m_steamIDRemote);
else
{
session.pendingSteam.Add(req.m_steamIDRemote);

PendingPlayerWindow.EnqueueJoinRequest(req.m_steamIDRemote, (joinReq, accepted) =>
{
if(joinReq.steamId.HasValue && accepted) AcceptPlayerJoinRequest(joinReq.steamId.Value);
});
}
session.knownUsers.Add(req.m_steamIDRemote);
session.NotifyChat();

Expand Down Expand Up @@ -64,6 +70,14 @@ public static void InitCallbacks()
});
}

public static void AcceptPlayerJoinRequest(CSteamID id)
{
SteamNetworking.AcceptP2PSessionWithUser(id);
Multiplayer.session.pendingSteam.Remove(id);

Messages.Message("MpSteamAccepted".Translate(), MessageTypeDefOf.PositiveEvent, false);
}

private static Stopwatch lastSteamUpdate = Stopwatch.StartNew();
private static bool lastLocalSteam; // running a server with steam networking
private static CSteamID? lastRemoteSteam; // connected to a server with steam networking
Expand Down
72 changes: 72 additions & 0 deletions Source/Client/Util/RectExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using UnityEngine;
using Verse;

namespace Multiplayer.Client
{
Expand Down Expand Up @@ -109,5 +110,76 @@ public static Rect AtY(this Rect rect, float y)
rect.y = y;
return rect;
}

public static Rect FitToText(this Rect rect, string text)
{
var textSize = Text.CalcSize(text);
rect.width = textSize.x;
rect.height = textSize.y;
return rect;
}

public static Rect MarginLeft(this Rect rect, float margin)
{
rect.x += margin;
rect.width -= margin;
return rect;
}

public static Rect MarginRight(this Rect rect, float margin)
{
rect.width -= margin;
return rect;
}

public static Rect MarginTop(this Rect rect, float margin)
{
rect.y += margin;
rect.height -= margin;
return rect;
}

public static Rect MarginBottom(this Rect rect, float margin)
{
rect.height -= margin;
return rect;
}

public static Rect CenteredButtonX(this Rect rect, int buttonIndex,
int buttonsCount,
float buttonWidth,
float pad = 10f)
{
rect.x += GenUI.GetCenteredButtonPos(buttonIndex, buttonsCount, rect.width, buttonWidth, pad);
rect.width = buttonWidth;
return rect;
}

public static Rect SpacedEvenlyX(this Rect rect, int buttonIndex, int buttonsCount, float buttonWidth)
{
float totalWidth = rect.width;
float pad = (float)(((double) totalWidth - (double)buttonsCount * (double) buttonWidth) / (double) (buttonsCount + 1));
rect.x += Mathf.Floor(pad + buttonIndex * (buttonWidth + pad));
rect.width = buttonWidth;
return rect;
}

public static Rect SpacedAroundX(this Rect rect, int buttonIndex, int buttonsCount, float buttonWidth)
{
float totalWidth = rect.width;
float pad = (float)(((double) totalWidth - (double)buttonsCount * (double) buttonWidth) / (double) (buttonsCount));
rect.x += Mathf.Floor(pad/2 + buttonIndex * (buttonWidth + pad));
rect.width = buttonWidth;
return rect;
}

public static Rect SpacedBetweenX(this Rect rect, int buttonIndex, int buttonsCount, float buttonWidth)
{
float totalWidth = rect.width;
float pad = (float)(((double) totalWidth - (double)buttonsCount * (double) buttonWidth) / (double) (buttonsCount - 1));
rect.x += Mathf.Floor(buttonIndex * (buttonWidth + pad));
rect.width = buttonWidth;
return rect;
}
}
}
10 changes: 1 addition & 9 deletions Source/Client/Windows/ChatWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ private void DrawInfo(Rect inRect)
SteamFriends.GetFriendPersonaName,
ref inRect,
ref steamScroll,
AcceptSteamPlayer,
SteamIntegration.AcceptPlayerJoinRequest,
true,
"MpSteamAcceptDesc".Translate()
);
Expand All @@ -174,14 +174,6 @@ private void ClickPlayer(PlayerInfo p)
}
}

private void AcceptSteamPlayer(CSteamID id)
{
SteamNetworking.AcceptP2PSessionWithUser(id);
Multiplayer.session.pendingSteam.Remove(id);

Messages.Message("MpSteamAccepted".Translate(), MessageTypeDefOf.PositiveEvent, false);
}

private Color GetColor(PlayerStatus status)
{
switch (status)
Expand Down
3 changes: 1 addition & 2 deletions Source/Client/Windows/ConnectingWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,7 @@ public class RejoiningWindow : BaseConnectingWindow

public class ConnectingWindow(string address, int port) : BaseConnectingWindow
{
protected override string ConnectingString =>
string.Format("MpConnectingTo".Translate("{0}", port), address);
protected override string ConnectingString => "MpConnectingTo".Translate(address, port);
}

public class SteamConnectingWindow(CSteamID hostId) : BaseConnectingWindow
Expand Down
2 changes: 2 additions & 0 deletions Source/Client/Windows/HostWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ private void TryHost()
else
HostFromSpSaveFile(settings);

// No need to return to the server browser since we successfully started a local server.
returnToServerBrowser = false;
Close();
}

Expand Down
192 changes: 192 additions & 0 deletions Source/Client/Windows/PendingPlayerWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#nullable enable
using System;
using System.Collections.Concurrent;
using LudeonTK;
using Multiplayer.Client.Util;
using Steamworks;
using UnityEngine;
using Verse;

namespace Multiplayer.Client.Windows;

public class PendingPlayerWindow : Window
{
private static PendingPlayerWindow? Opened => Find.WindowStack?.WindowOfType<PendingPlayerWindow>();

public static void EnqueueJoinRequest(string name, Action<Request, bool>? callback = null) => Request.Enqueue(name, callback);
public static void EnqueueJoinRequest(CSteamID steamId, Action<Request, bool>? callback = null) => Request.Enqueue(steamId, callback);

public record Request
{
public readonly CSteamID? steamId;
public readonly string name;
private readonly Action<Request, bool>? callback;

private Request(CSteamID? steamId, string name, Action<Request, bool>? callback)
{
this.steamId = steamId;
this.name = name;
this.callback = callback;
}

internal void RunCallback(bool accepted)
{
try
{
callback?.Invoke(this, accepted);
}
catch (Exception e)
{
MpLog.Warn($"Exception invoking join request callback for {steamId}:{name}: {e}");
}
}

private void Enqueue()
{
var window = Opened;
if (window != null)
{
window.queue.Enqueue(this);
return;
}

window = new PendingPlayerWindow();
window.queue.Enqueue(this);
Find.WindowStack.Add(window);
}

public static void Enqueue(string name, Action<Request, bool>? callback = null) =>
new Request(null, name, callback).Enqueue();

public static void Enqueue(CSteamID steamId, Action<Request, bool>? callback = null) =>
new Request(steamId, SteamFriends.GetFriendPersonaName(steamId), callback).Enqueue();
}

private const float BtnMargin = 8f;
private const float BtnWidth = 65f;
private const float AnimTimeSecs = .7f;
private float startTime;

private readonly ConcurrentQueue<Request> queue = [];
private Request? req;

private PendingPlayerWindow()
{
preventCameraMotion = false;
focusWhenOpened = false;
closeOnClickedOutside = false;
closeOnCancel = false;
closeOnAccept = false;
layer = WindowLayer.GameUI;
}

public override Vector2 InitialSize => new(200f, req?.steamId.HasValue == true ? 320f : 245f);
public override float Margin => 4f;

public override void PreOpen()
{
startTime = Time.time;
if (req == null) ShowNextRequestOrClose();
base.PreOpen();
}

public override void SetInitialSizeAndPosition()
{
// Add scaled 1f to hide the right border of the window
windowRect = new Rect(UI.screenWidth - InitialSize.y + UIScaling.AdjustCoordToUIScalingCeil(1f),
InitialSize.x, InitialSize.y, 96f);
}

private void UpdateWindowRect()
{
var arrivedAgo = Time.time - startTime;
var animFinished = arrivedAgo > AnimTimeSecs;
if (!animFinished)
{
var timeProgress = arrivedAgo / AnimTimeSecs;
var posProgress = 1 - Math.Pow(1 - timeProgress, 2.5);
windowRect.x = UI.screenWidth - (float)posProgress * InitialSize.y +
UIScaling.AdjustCoordToUIScalingCeil(1f);
} else SetInitialSizeAndPosition();
}

public override void WindowOnGUI()
{
UpdateWindowRect();
windowRect = GUI.Window(ID, windowRect, innerWindowOnGUICached, "", windowDrawing.EmptyStyle);
}

public override void DoWindowContents(Rect inRect)
{
// This should not happen
if (req == null) return;
if (req.steamId.HasValue) {
var avatarTex = SteamImages.GetTexture(SteamFriends.GetLargeFriendAvatar(SteamUser.GetSteamID()));
var avatarRect = new Rect(0, 0, 80, 80).CenteredOnYIn(inRect).Right(4);
InvisibleOpenSteamProfileButton(avatarRect, req.steamId, doMouseoverSound: false);
if (avatarTex != null)
GUI.DrawTextureWithTexCoords(avatarRect, avatarTex, new Rect(0, 1, 1, -1));
inRect.xMin = avatarRect.xMax + 6f;
}
else
{
inRect.xMin += 15f;
}

using (MpStyle.Set(TextAnchor.UpperLeft).Set(WordWrap.NoWrap).Set(GameFont.Medium))
{
var textRect = inRect;

string usernameClamped = Text.ClampTextWithEllipsis(textRect, req.name);
var nameTextRect = textRect.FitToText(usernameClamped);

var showTooltip = usernameClamped.Length != req.name.Length;
if (req.steamId.HasValue || showTooltip)
Widgets.DrawHighlightIfMouseover(nameTextRect.ExpandedBy(3f, 0f));
if (showTooltip) TooltipHandler.TipRegion(nameTextRect, new TipSignal(req.name));

Widgets.Label(nameTextRect, usernameClamped);
InvisibleOpenSteamProfileButton(nameTextRect, req.steamId);
inRect = inRect.MarginTop(nameTextRect.height + Text.SpaceBetweenLines);
}

using (MpStyle.Set(TextAnchor.UpperLeft).Set(WordWrap.NoWrap).Set(GameFont.Small))
Widgets.Label(inRect, "MpJoinRequestDesc".Translate());

var btnGroupRect = inRect.MarginTop(Text.LineHeightOf(GameFont.Small) + BtnMargin).MarginBottom(4f).MarginRight(6f);

var btnOkRect = btnGroupRect.Width(BtnWidth);
if (Widgets.ButtonText(btnOkRect, "Accept".Translate(), overrideTextAnchor: TextAnchor.MiddleCenter))
{
req.RunCallback(true);
ShowNextRequestOrClose();
}

var btnNoRect = btnOkRect.Right(btnOkRect.width + BtnMargin);
if (Widgets.ButtonText(btnNoRect, "RejectLetter".Translate(), overrideTextAnchor: TextAnchor.MiddleCenter)) {
req.RunCallback(false);
ShowNextRequestOrClose();
}
}

// Try our best to make sure there is no possibility of missing a request
public override bool OnCloseRequest() => queue.IsEmpty;

private static void InvisibleOpenSteamProfileButton(Rect rect, CSteamID? steamId, bool doMouseoverSound = true)
{
if (steamId.HasValue && Widgets.ButtonInvisible(rect, doMouseoverSound))
SteamFriends.ActivateGameOverlayToUser("steamid", steamId.Value);
}

private void ShowNextRequestOrClose()
{
if (queue.TryDequeue(out req))
{
// Window size differs if a Steam ID is present (extra space for avatar)
UpdateWindowRect();
return;
}

Close();
}
}
Loading