2f107db...5202af9
This commit is contained in:
@@ -1,7 +1,22 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class BannedPlayer
|
||||
{
|
||||
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string ip, ulong steamID)
|
||||
{
|
||||
this.Name = name;
|
||||
this.SteamID = steamID;
|
||||
this.IsRangeBan = isRangeBan;
|
||||
this.IP = ip;
|
||||
this.UniqueIdentifier = uniqueIdentifier;
|
||||
}
|
||||
}
|
||||
|
||||
partial class BanList
|
||||
{
|
||||
private GUIComponent banFrame;
|
||||
@@ -11,12 +26,27 @@ namespace Barotrauma.Networking
|
||||
get { return banFrame; }
|
||||
}
|
||||
|
||||
public List<UInt16> localRemovedBans = new List<UInt16>();
|
||||
public List<UInt16> localRangeBans = new List<UInt16>();
|
||||
|
||||
private void RecreateBanFrame()
|
||||
{
|
||||
if (banFrame != null)
|
||||
{
|
||||
var parent = banFrame.Parent;
|
||||
parent.RemoveChild(banFrame);
|
||||
CreateBanFrame(parent);
|
||||
}
|
||||
}
|
||||
|
||||
public GUIComponent CreateBanFrame(GUIComponent parent)
|
||||
{
|
||||
banFrame = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform, Anchor.Center));
|
||||
|
||||
foreach (BannedPlayer bannedPlayer in bannedPlayers)
|
||||
{
|
||||
if (localRemovedBans.Contains(bannedPlayer.UniqueIdentifier)) continue;
|
||||
|
||||
var playerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.25f), ((GUIListBox)banFrame).Content.RectTransform) { MinSize = new Point(0, 70) }, style: null)
|
||||
{
|
||||
UserData = banFrame
|
||||
@@ -28,8 +58,10 @@ namespace Barotrauma.Networking
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
string ip = bannedPlayer.IP;
|
||||
if (localRangeBans.Contains(bannedPlayer.UniqueIdentifier)) ip = ToRange(ip);
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), paddedPlayerFrame.RectTransform),
|
||||
bannedPlayer.IP + " (" + bannedPlayer.Name + ")");
|
||||
bannedPlayer.Name + " (" + ip + ")");
|
||||
|
||||
var removeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.4f), paddedPlayerFrame.RectTransform, Anchor.TopRight), TextManager.Get("BanListRemove"))
|
||||
{
|
||||
@@ -71,14 +103,9 @@ namespace Barotrauma.Networking
|
||||
BannedPlayer banned = obj as BannedPlayer;
|
||||
if (banned == null) return false;
|
||||
|
||||
RemoveBan(banned);
|
||||
localRemovedBans.Add(banned.UniqueIdentifier);
|
||||
|
||||
if (banFrame != null)
|
||||
{
|
||||
var parent = banFrame.Parent;
|
||||
parent.RemoveChild(banFrame);
|
||||
CreateBanFrame(parent);
|
||||
}
|
||||
RecreateBanFrame();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -88,14 +115,9 @@ namespace Barotrauma.Networking
|
||||
BannedPlayer banned = obj as BannedPlayer;
|
||||
if (banned == null) return false;
|
||||
|
||||
RangeBan(banned);
|
||||
localRangeBans.Add(banned.UniqueIdentifier);
|
||||
|
||||
if (banFrame != null)
|
||||
{
|
||||
var parent = banFrame.Parent;
|
||||
parent.RemoveChild(banFrame);
|
||||
CreateBanFrame(parent);
|
||||
}
|
||||
RecreateBanFrame();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -106,5 +128,66 @@ namespace Barotrauma.Networking
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClientAdminRead(NetBuffer incMsg)
|
||||
{
|
||||
bool hasPermission = incMsg.ReadBoolean();
|
||||
if (!hasPermission)
|
||||
{
|
||||
incMsg.ReadPadBits();
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOwner = incMsg.ReadBoolean();
|
||||
incMsg.ReadPadBits();
|
||||
|
||||
bannedPlayers.Clear();
|
||||
Int32 bannedPlayerCount = incMsg.ReadVariableInt32();
|
||||
for (int i = 0; i < bannedPlayerCount; i++)
|
||||
{
|
||||
string name = incMsg.ReadString();
|
||||
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
|
||||
bool isRangeBan = incMsg.ReadBoolean(); incMsg.ReadPadBits();
|
||||
|
||||
string ip = "";
|
||||
UInt64 steamID = 0;
|
||||
if (isOwner)
|
||||
{
|
||||
ip = incMsg.ReadString();
|
||||
steamID = incMsg.ReadUInt64();
|
||||
}
|
||||
else
|
||||
{
|
||||
ip = "IP concealed by host";
|
||||
steamID = 0;
|
||||
}
|
||||
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, ip, steamID));
|
||||
}
|
||||
|
||||
if (banFrame != null)
|
||||
{
|
||||
var parent = banFrame.Parent;
|
||||
parent.RemoveChild(banFrame);
|
||||
CreateBanFrame(parent);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientAdminWrite(NetBuffer outMsg)
|
||||
{
|
||||
outMsg.Write((UInt16)localRemovedBans.Count);
|
||||
foreach (UInt16 uniqueId in localRemovedBans)
|
||||
{
|
||||
outMsg.Write(uniqueId);
|
||||
}
|
||||
|
||||
outMsg.Write((UInt16)localRangeBans.Count);
|
||||
foreach (UInt16 uniqueId in localRangeBans)
|
||||
{
|
||||
outMsg.Write(uniqueId);
|
||||
}
|
||||
|
||||
localRemovedBans.Clear();
|
||||
localRangeBans.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
orderOption = order.Options[optionIndex];
|
||||
}
|
||||
txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.RoomName, orderOption);
|
||||
txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.RoomName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
|
||||
|
||||
if (order.TargetAllCharacters)
|
||||
{
|
||||
@@ -99,7 +99,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
return;
|
||||
}
|
||||
GameMain.Client.ServerLog?.WriteLine(txt, messageType);
|
||||
GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
|
||||
break;
|
||||
default:
|
||||
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter);
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
using Barotrauma.Sounds;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
struct TempClient
|
||||
{
|
||||
public string Name;
|
||||
public byte ID;
|
||||
public UInt16 CharacterID;
|
||||
public bool Muted;
|
||||
}
|
||||
|
||||
partial class Client : IDisposable
|
||||
{
|
||||
public VoipSound VoipSound
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private bool mutedLocally;
|
||||
public bool MutedLocally
|
||||
{
|
||||
get { return mutedLocally; }
|
||||
set
|
||||
{
|
||||
if (mutedLocally == value) { return; }
|
||||
mutedLocally = value;
|
||||
#if CLIENT
|
||||
GameMain.NetLobbyScreen.SetPlayerVoiceIconState(this, muted, mutedLocally);
|
||||
GameMain.GameSession?.CrewManager?.SetPlayerVoiceIconState(this, muted, mutedLocally);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSoundPosition()
|
||||
{
|
||||
if (VoipSound != null)
|
||||
{
|
||||
if (!VoipSound.IsPlaying)
|
||||
{
|
||||
DebugConsole.Log("Destroying voipsound");
|
||||
VoipSound.Dispose();
|
||||
VoipSound = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
VoipSound.SetPosition(new Vector3(character.WorldPosition.X, character.WorldPosition.Y, 0.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
VoipSound.SetPosition(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
VoipQueue = null; VoipSound = null;
|
||||
if (ID == GameMain.Client.ID) return;
|
||||
VoipQueue = new VoipQueue(ID, false, true);
|
||||
GameMain.Client.VoipClient.RegisterQueue(VoipQueue);
|
||||
VoipSound = null;
|
||||
}
|
||||
|
||||
public void SetPermissions(ClientPermissions permissions, List<string> permittedConsoleCommands)
|
||||
{
|
||||
List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
|
||||
foreach (string commandName in permittedConsoleCommands)
|
||||
{
|
||||
var consoleCommand = DebugConsole.Commands.Find(c => c.names.Contains(commandName));
|
||||
if (consoleCommand != null)
|
||||
{
|
||||
permittedCommands.Add(consoleCommand);
|
||||
}
|
||||
}
|
||||
SetPermissions(permissions, permittedCommands);
|
||||
}
|
||||
|
||||
public void SetPermissions(ClientPermissions permissions, List<DebugConsole.Command> permittedConsoleCommands)
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Permissions = permissions;
|
||||
PermittedConsoleCommands = new List<DebugConsole.Command>(permittedConsoleCommands);
|
||||
}
|
||||
|
||||
public void GivePermission(ClientPermissions permission)
|
||||
{
|
||||
if (GameMain.Client == null || !GameMain.Client.HasPermission(ClientPermissions.ManagePermissions))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Permissions.HasFlag(permission)) Permissions |= permission;
|
||||
}
|
||||
|
||||
public void RemovePermission(ClientPermissions permission)
|
||||
{
|
||||
if (GameMain.Client == null || !GameMain.Client.HasPermission(ClientPermissions.ManagePermissions))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Permissions.HasFlag(permission)) Permissions &= ~permission;
|
||||
}
|
||||
|
||||
public bool HasPermission(ClientPermissions permission)
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Permissions.HasFlag(permission);
|
||||
}
|
||||
|
||||
partial void DisposeProjSpecific()
|
||||
{
|
||||
if (VoipQueue != null)
|
||||
{
|
||||
GameMain.Client.VoipClient.UnregisterQueue(VoipQueue);
|
||||
}
|
||||
if (VoipSound != null)
|
||||
{
|
||||
VoipSound.Dispose();
|
||||
VoipSound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ namespace Barotrauma
|
||||
{
|
||||
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer message, float sendingTime)
|
||||
{
|
||||
if (GameMain.Server != null) return;
|
||||
|
||||
bool remove = message.ReadBoolean();
|
||||
|
||||
if (remove)
|
||||
|
||||
@@ -167,21 +167,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
public FileReceiver()
|
||||
{
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
throw new InvalidOperationException("Creating a file receiver is not allowed when a server is running.");
|
||||
}
|
||||
|
||||
activeTransfers = new List<FileTransferIn>();
|
||||
}
|
||||
|
||||
public void ReadMessage(NetIncomingMessage inc)
|
||||
{
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
throw new InvalidOperationException("Receiving files when a server is running is not allowed");
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.Assert(!activeTransfers.Any(t =>
|
||||
t.Status == FileTransferStatus.Error ||
|
||||
t.Status == FileTransferStatus.Canceled ||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,244 +0,0 @@
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class GameServer : NetworkMember
|
||||
{
|
||||
private NetStats netStats;
|
||||
|
||||
private GUIScrollBar clientListScrollBar;
|
||||
|
||||
void InitProjSpecific()
|
||||
{
|
||||
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, inGameHUD.RectTransform),
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
var endRoundButton = new GUIButton(new RectTransform(new Vector2(0.1f, 0.6f), buttonContainer.RectTransform) { MinSize = new Point(150, 0) },
|
||||
TextManager.Get("EndRound"))
|
||||
{
|
||||
OnClicked = (btn, userdata) => { EndGame(); return true; }
|
||||
};
|
||||
|
||||
showLogButton = new GUIButton(new RectTransform(new Vector2(0.1f, 0.6f), buttonContainer.RectTransform) { MinSize = new Point(150, 0) },
|
||||
TextManager.Get("ServerLog"))
|
||||
{
|
||||
OnClicked = (GUIButton button, object userData) =>
|
||||
{
|
||||
if (ServerLog.LogFrame == null)
|
||||
{
|
||||
ServerLog.CreateLogFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerLog.LogFrame = null;
|
||||
GUI.KeyboardDispatcher.Subscriber = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GUIButton settingsButton = new GUIButton(new RectTransform(new Vector2(0.1f, 0.6f), buttonContainer.RectTransform) { MinSize = new Point(150, 0) },
|
||||
TextManager.Get("ServerSettingsButton"))
|
||||
{
|
||||
OnClicked = ToggleSettingsFrame,
|
||||
UserData = "settingsButton"
|
||||
};
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
base.AddToGUIUpdateList();
|
||||
settingsFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
|
||||
{
|
||||
base.Draw(spriteBatch);
|
||||
if (!ShowNetStats) return;
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, "Unique Events: " + entityEventManager.UniqueEvents.Count, new Vector2(10, 50), Color.White);
|
||||
|
||||
int width = 200, height = 300;
|
||||
int x = GameMain.GraphicsWidth - width, y = (int)(GameMain.GraphicsHeight * 0.3f);
|
||||
|
||||
if (clientListScrollBar == null)
|
||||
{
|
||||
clientListScrollBar = new GUIScrollBar(new RectTransform(new Point(10, height), GUI.Canvas) { AbsoluteOffset = new Point(x + width - 10, y) });
|
||||
}
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black * 0.7f, true);
|
||||
GUI.Font.DrawString(spriteBatch, "Network statistics:", new Vector2(x + 10, y + 10), Color.White);
|
||||
|
||||
GUI.SmallFont.DrawString(spriteBatch, "Connections: " + server.ConnectionsCount, new Vector2(x + 10, y + 30), Color.White);
|
||||
GUI.SmallFont.DrawString(spriteBatch, "Received bytes: " + MathUtils.GetBytesReadable(server.Statistics.ReceivedBytes), new Vector2(x + 10, y + 45), Color.White);
|
||||
GUI.SmallFont.DrawString(spriteBatch, "Received packets: " + server.Statistics.ReceivedPackets, new Vector2(x + 10, y + 60), Color.White);
|
||||
|
||||
GUI.SmallFont.DrawString(spriteBatch, "Sent bytes: " + MathUtils.GetBytesReadable(server.Statistics.SentBytes), new Vector2(x + 10, y + 75), Color.White);
|
||||
GUI.SmallFont.DrawString(spriteBatch, "Sent packets: " + server.Statistics.SentPackets, new Vector2(x + 10, y + 90), Color.White);
|
||||
|
||||
int resentMessages = 0;
|
||||
|
||||
int clientListHeight = connectedClients.Count * 40;
|
||||
float scrollBarHeight = (height - 110) / (float)Math.Max(clientListHeight, 110);
|
||||
|
||||
if (clientListScrollBar.BarSize != scrollBarHeight)
|
||||
{
|
||||
clientListScrollBar.BarSize = scrollBarHeight;
|
||||
}
|
||||
|
||||
int startY = y + 110;
|
||||
y = (startY - (int)(clientListScrollBar.BarScroll * (clientListHeight - (height - 110))));
|
||||
foreach (Client c in connectedClients)
|
||||
{
|
||||
Color clientColor = c.Connection.AverageRoundtripTime > 0.3f ? Color.Red : Color.White;
|
||||
|
||||
if (y >= startY && y < startY + height - 120)
|
||||
{
|
||||
GUI.SmallFont.DrawString(spriteBatch, c.Name + " (" + c.Connection.RemoteEndPoint.Address.ToString() + ")", new Vector2(x + 10, y), clientColor);
|
||||
GUI.SmallFont.DrawString(spriteBatch, "Ping: " + (int)(c.Connection.AverageRoundtripTime * 1000.0f) + " ms", new Vector2(x + 20, y + 10), clientColor);
|
||||
}
|
||||
if (y + 25 >= startY && y < startY + height - 130) GUI.SmallFont.DrawString(spriteBatch, "Resent messages: " + c.Connection.Statistics.ResentMessages, new Vector2(x + 20, y + 20), clientColor);
|
||||
|
||||
resentMessages += (int)c.Connection.Statistics.ResentMessages;
|
||||
|
||||
y += 40;
|
||||
}
|
||||
|
||||
clientListScrollBar.UpdateManually(1.0f / 60.0f);
|
||||
clientListScrollBar.DrawManually(spriteBatch);
|
||||
|
||||
netStats.AddValue(NetStats.NetStatType.ResentMessages, Math.Max(resentMessages, 0));
|
||||
netStats.AddValue(NetStats.NetStatType.SentBytes, server.Statistics.SentBytes);
|
||||
netStats.AddValue(NetStats.NetStatType.ReceivedBytes, server.Statistics.ReceivedBytes);
|
||||
|
||||
netStats.Draw(spriteBatch, new Rectangle(200, 0, 800, 200), this);
|
||||
}
|
||||
|
||||
|
||||
private void UpdateFileTransferIndicator(Client client)
|
||||
{
|
||||
var transfers = fileSender.ActiveTransfers.FindAll(t => t.Connection == client.Connection);
|
||||
|
||||
var clientNameBox = GameMain.NetLobbyScreen.PlayerList.Content.FindChild(client.Name);
|
||||
|
||||
var clientInfo = clientNameBox.FindChild("filetransfer");
|
||||
if (clientInfo == null)
|
||||
{
|
||||
clientInfo = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.9f), clientNameBox.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.1f, 0.0f) }, style: null)
|
||||
{
|
||||
UserData = "filetransfer"
|
||||
};
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), clientInfo.RectTransform),
|
||||
"", textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
|
||||
{
|
||||
TextScale = 0.8f,
|
||||
TextGetter = () =>
|
||||
{
|
||||
string txt = "";
|
||||
if (transfers.Count > 0)
|
||||
{
|
||||
txt += transfers[0].FileName + " ";
|
||||
if (transfers.Count > 1) txt += "+ " + (transfers.Count - 1) + " others ";
|
||||
}
|
||||
txt += "(" + MathUtils.GetBytesReadable(transfers.Sum(t => t.SentOffset)) + " / " + MathUtils.GetBytesReadable(transfers.Sum(t => t.Data.Length)) + ")";
|
||||
return txt;
|
||||
}
|
||||
};
|
||||
|
||||
var progressBar = new GUIProgressBar(new RectTransform(new Vector2(0.8f, 0.5f), clientInfo.RectTransform, Anchor.BottomLeft),
|
||||
barSize: 0.0f, color: Color.Green)
|
||||
{
|
||||
IsHorizontal = true,
|
||||
ProgressGetter = () => { return transfers.Sum(t => t.Progress) / transfers.Count; }
|
||||
};
|
||||
|
||||
var cancelButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.5f), clientInfo.RectTransform, Anchor.BottomRight), "X")
|
||||
{
|
||||
OnClicked = (GUIButton button, object userdata) =>
|
||||
{
|
||||
transfers.ForEach(t => fileSender.CancelTransfer(t));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (transfers.Count == 0)
|
||||
{
|
||||
clientInfo.Parent.RemoveChild(clientInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SelectCrewCharacter(Character character, GUIComponent characterFrame)
|
||||
{
|
||||
if (character == null) return false;
|
||||
|
||||
if (character != myCharacter)
|
||||
{
|
||||
var banButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.15f), characterFrame.RectTransform, Anchor.BottomRight),
|
||||
TextManager.Get("Ban"))
|
||||
{
|
||||
UserData = character.Name,
|
||||
OnClicked = GameMain.NetLobbyScreen.BanPlayer
|
||||
};
|
||||
var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.15f), characterFrame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.16f) },
|
||||
TextManager.Get("BanRange"))
|
||||
{
|
||||
UserData = character.Name,
|
||||
OnClicked = GameMain.NetLobbyScreen.BanPlayerRange
|
||||
};
|
||||
var kickButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.15f), characterFrame.RectTransform, Anchor.BottomLeft),
|
||||
TextManager.Get("Kick"))
|
||||
{
|
||||
UserData = character.Name,
|
||||
OnClicked = GameMain.NetLobbyScreen.KickPlayer
|
||||
};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private GUIMessageBox upnpBox;
|
||||
void InitUPnP()
|
||||
{
|
||||
server.UPnP.ForwardPort(NetPeerConfiguration.Port, "barotrauma");
|
||||
if (Steam.SteamManager.USE_STEAM)
|
||||
{
|
||||
server.UPnP.ForwardPort(QueryPort, "barotrauma");
|
||||
}
|
||||
|
||||
upnpBox = new GUIMessageBox(TextManager.Get("PleaseWaitUPnP"), TextManager.Get("AttemptingUPnP"), new string[] { TextManager.Get("Cancel") });
|
||||
upnpBox.Buttons[0].OnClicked = upnpBox.Close;
|
||||
}
|
||||
|
||||
bool DiscoveringUPnP()
|
||||
{
|
||||
return server.UPnP.Status == UPnPStatus.Discovering && GUIMessageBox.VisibleBox == upnpBox;
|
||||
}
|
||||
|
||||
void FinishUPnP()
|
||||
{
|
||||
upnpBox.Close(null, null);
|
||||
|
||||
if (server.UPnP.Status == UPnPStatus.NotAvailable)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("UPnPUnavailable"));
|
||||
}
|
||||
else if (server.UPnP.Status == UPnPStatus.Discovering)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("UPnPTimedOut"));
|
||||
}
|
||||
}
|
||||
|
||||
public bool StartGameClicked(GUIButton button, object obj)
|
||||
{
|
||||
if (initiatedStartGame || gameStarted) { return false; }
|
||||
return StartGame();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,649 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class GameServer : NetworkMember, ISerializableEntity
|
||||
{
|
||||
private GUIButton settingsFrame;
|
||||
private GUIFrame[] settingsTabs;
|
||||
private int settingsTabIndex;
|
||||
|
||||
enum SettingsTab
|
||||
{
|
||||
Rounds,
|
||||
Server,
|
||||
Banlist,
|
||||
Whitelist
|
||||
}
|
||||
|
||||
private void CreateSettingsFrame()
|
||||
{
|
||||
settingsFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker")
|
||||
{
|
||||
OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) ToggleSettingsFrame(btn, userdata); return true; }
|
||||
};
|
||||
new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null)
|
||||
{
|
||||
OnClicked = ToggleSettingsFrame
|
||||
};
|
||||
|
||||
GUIFrame innerFrame = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.7f), settingsFrame.RectTransform, Anchor.Center) { MinSize = new Point(400, 430) });
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), innerFrame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), "Settings", font: GUI.LargeFont);
|
||||
|
||||
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), paddedFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.1f) }, isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.01f
|
||||
};
|
||||
|
||||
var tabValues = Enum.GetValues(typeof(SettingsTab)).Cast<SettingsTab>().ToArray();
|
||||
string[] tabNames = new string[tabValues.Count()];
|
||||
for (int i = 0; i < tabNames.Length; i++)
|
||||
{
|
||||
tabNames[i] = TextManager.Get("ServerSettings" + tabValues[i] + "Tab");
|
||||
}
|
||||
settingsTabs = new GUIFrame[tabNames.Length];
|
||||
for (int i = 0; i < tabNames.Length; i++)
|
||||
{
|
||||
settingsTabs[i] = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.79f), paddedFrame.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.05f) },
|
||||
style: "InnerFrame");
|
||||
|
||||
var tabButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform), tabNames[i])
|
||||
{
|
||||
UserData = i,
|
||||
OnClicked = SelectSettingsTab
|
||||
};
|
||||
}
|
||||
|
||||
SelectSettingsTab(null, 0);
|
||||
|
||||
var closeButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.05f), paddedFrame.RectTransform, Anchor.BottomRight), TextManager.Get("Close"))
|
||||
{
|
||||
OnClicked = ToggleSettingsFrame
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// game settings
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
var roundsTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsSubSelection"));
|
||||
var selectionFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var selectionTick = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), selectionFrame.RectTransform), ((SelectionMode)i).ToString(), font: GUI.SmallFont)
|
||||
{
|
||||
Selected = i == (int)subSelectionMode,
|
||||
OnSelected = SwitchSubSelection,
|
||||
UserData = (SelectionMode)i,
|
||||
};
|
||||
}
|
||||
GUITickBox.CreateRadioButtonGroup(selectionFrame.Children.Select(c => c as GUITickBox));
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsModeSelection"));
|
||||
selectionFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var selectionTick = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), selectionFrame.RectTransform), ((SelectionMode)i).ToString(), font: GUI.SmallFont)
|
||||
{
|
||||
Selected = i == (int)modeSelectionMode,
|
||||
OnSelected = SwitchModeSelection,
|
||||
UserData = (SelectionMode)i
|
||||
};
|
||||
}
|
||||
GUITickBox.CreateRadioButtonGroup(selectionFrame.Children.Select(c => c as GUITickBox));
|
||||
|
||||
var endBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
|
||||
TextManager.Get("ServerSettingsEndRoundWhenDestReached"))
|
||||
{
|
||||
Selected = EndRoundAtLevelEnd,
|
||||
OnSelected = (GUITickBox) => { EndRoundAtLevelEnd = GUITickBox.Selected; return true; }
|
||||
};
|
||||
|
||||
var endVoteBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
|
||||
TextManager.Get("ServerSettingsEndRoundVoting"))
|
||||
{
|
||||
Selected = Voting.AllowEndVoting,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
Voting.AllowEndVoting = !Voting.AllowEndVoting;
|
||||
GameMain.Server.UpdateVoteStatus();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CreateLabeledSlider(roundsTab, "ServerSettingsEndRoundVotesRequired", out GUIScrollBar slider, out GUITextBlock sliderLabel);
|
||||
|
||||
string endRoundLabel = sliderLabel.Text;
|
||||
slider.Step = 0.2f;
|
||||
slider.BarScroll = (EndVoteRequiredRatio - 0.5f) * 2.0f;
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
EndVoteRequiredRatio = barScroll / 2.0f + 0.5f;
|
||||
((GUITextBlock)scrollBar.UserData).Text = endRoundLabel + (int)MathUtils.Round(EndVoteRequiredRatio * 100.0f, 10.0f) + " %";
|
||||
return true;
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
var respawnBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowRespawning"))
|
||||
{
|
||||
Selected = AllowRespawn,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
AllowRespawn = !AllowRespawn;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CreateLabeledSlider(roundsTab, "ServerSettingsRespawnInterval", out slider, out sliderLabel);
|
||||
string intervalLabel = sliderLabel.Text;
|
||||
slider.Step = 0.05f;
|
||||
slider.BarScroll = RespawnInterval / 600.0f;
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
GUITextBlock text = scrollBar.UserData as GUITextBlock;
|
||||
RespawnInterval = Math.Max(barScroll * 600.0f, 10.0f);
|
||||
text.Text = intervalLabel + ToolBox.SecondsToReadableTime(RespawnInterval);
|
||||
return true;
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
var minRespawnText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), "")
|
||||
{
|
||||
ToolTip = TextManager.Get("ServerSettingsMinRespawnToolTip")
|
||||
};
|
||||
|
||||
string minRespawnLabel = TextManager.Get("ServerSettingsMinRespawn");
|
||||
CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel);
|
||||
slider.ToolTip = minRespawnText.ToolTip;
|
||||
slider.UserData = minRespawnText;
|
||||
slider.Step = 0.1f;
|
||||
slider.BarScroll = MinRespawnRatio;
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
MinRespawnRatio = barScroll;
|
||||
((GUITextBlock)scrollBar.UserData).Text = minRespawnLabel + (int)MathUtils.Round(MinRespawnRatio * 100.0f, 10.0f) + " %";
|
||||
return true;
|
||||
};
|
||||
slider.OnMoved(slider, MinRespawnRatio);
|
||||
|
||||
var respawnDurationText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), "")
|
||||
{
|
||||
ToolTip = TextManager.Get("ServerSettingsRespawnDurationToolTip")
|
||||
};
|
||||
|
||||
string respawnDurationLabel = TextManager.Get("ServerSettingsRespawnDuration");
|
||||
CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel);
|
||||
slider.ToolTip = respawnDurationText.ToolTip;
|
||||
slider.UserData = respawnDurationText;
|
||||
slider.Step = 0.1f;
|
||||
slider.BarScroll = MaxTransportTime <= 0.0f ? 1.0f : (MaxTransportTime - 60.0f) / 600.0f;
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
if (barScroll == 1.0f)
|
||||
{
|
||||
MaxTransportTime = 0;
|
||||
((GUITextBlock)scrollBar.UserData).Text = respawnDurationLabel + "unlimited";
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxTransportTime = barScroll * 600.0f + 60.0f;
|
||||
((GUITextBlock)scrollBar.UserData).Text = respawnDurationLabel + ToolBox.SecondsToReadableTime(MaxTransportTime);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
var buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), roundsTab.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
var monsterButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonHolder.RectTransform),
|
||||
TextManager.Get("ServerSettingsMonsterSpawns"))
|
||||
{
|
||||
Enabled = !GameStarted
|
||||
};
|
||||
var monsterFrame = new GUIListBox(new RectTransform(new Vector2(0.6f, 0.7f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.BottomLeft, Pivot.BottomRight))
|
||||
{
|
||||
Visible = false
|
||||
};
|
||||
monsterButton.UserData = monsterFrame;
|
||||
monsterButton.OnClicked = (button, obj) =>
|
||||
{
|
||||
if (gameStarted)
|
||||
{
|
||||
((GUIComponent)obj).Visible = false;
|
||||
button.Enabled = false;
|
||||
return true;
|
||||
}
|
||||
((GUIComponent)obj).Visible = !((GUIComponent)obj).Visible;
|
||||
return true;
|
||||
};
|
||||
List<string> monsterNames = monsterEnabled.Keys.ToList();
|
||||
foreach (string s in monsterNames)
|
||||
{
|
||||
var monsterEnabledBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), monsterFrame.Content.RectTransform) { MinSize = new Point(0, 25) },
|
||||
label: s)
|
||||
{
|
||||
Selected = monsterEnabled[s],
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
if (gameStarted)
|
||||
{
|
||||
monsterFrame.Visible = false;
|
||||
monsterButton.Enabled = false;
|
||||
return true;
|
||||
}
|
||||
monsterEnabled[s] = !monsterEnabled[s];
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var cargoButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonHolder.RectTransform),
|
||||
TextManager.Get("ServerSettingsAdditionalCargo"))
|
||||
{
|
||||
Enabled = !GameStarted
|
||||
};
|
||||
var cargoFrame = new GUIListBox(new RectTransform(new Vector2(0.6f, 0.7f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.BottomRight, Pivot.BottomLeft))
|
||||
{
|
||||
Visible = false
|
||||
};
|
||||
cargoButton.UserData = cargoFrame;
|
||||
cargoButton.OnClicked = (button, obj) =>
|
||||
{
|
||||
if (gameStarted)
|
||||
{
|
||||
((GUIComponent)obj).Visible = false;
|
||||
button.Enabled = false;
|
||||
return true;
|
||||
}
|
||||
((GUIComponent)obj).Visible = !((GUIComponent)obj).Visible;
|
||||
return true;
|
||||
};
|
||||
|
||||
foreach (MapEntityPrefab pf in MapEntityPrefab.List)
|
||||
{
|
||||
ItemPrefab ip = pf as ItemPrefab;
|
||||
|
||||
if (ip == null || (!ip.CanBeBought && !ip.Tags.Contains("smallitem"))) continue;
|
||||
|
||||
var itemFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), cargoFrame.Content.RectTransform) { MinSize = new Point(0, 30) }, isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
UserData = cargoFrame,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
|
||||
if (ip.InventoryIcon != null || ip.sprite != null)
|
||||
{
|
||||
GUIImage img = new GUIImage(new RectTransform(new Point(itemFrame.Rect.Height), itemFrame.RectTransform),
|
||||
ip.InventoryIcon ?? ip.sprite, scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
img.Color = img.Sprite == ip.InventoryIcon ? ip.InventoryIconColor : ip.SpriteColor;
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), itemFrame.RectTransform),
|
||||
ip.Name, font: GUI.SmallFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
extraCargo.TryGetValue(ip, out int cargoVal);
|
||||
var amountInput = new GUINumberInput(new RectTransform(new Vector2(0.3f, 1.0f), itemFrame.RectTransform),
|
||||
GUINumberInput.NumberType.Int, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
MinValueInt = 0,
|
||||
MaxValueInt = 100,
|
||||
IntValue = cargoVal
|
||||
};
|
||||
amountInput.OnValueChanged += (numberInput) =>
|
||||
{
|
||||
if (extraCargo.ContainsKey(ip))
|
||||
{
|
||||
extraCargo[ip] = numberInput.IntValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
extraCargo.Add(ip, numberInput.IntValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// server settings
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
var serverTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Server].RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
|
||||
string autoRestartDelayLabel = TextManager.Get("ServerSettingsAutoRestartDelay");
|
||||
var startIntervalText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), autoRestartDelayLabel);
|
||||
var startIntervalSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), barSize: 0.1f)
|
||||
{
|
||||
UserData = startIntervalText,
|
||||
Step = 0.05f,
|
||||
BarScroll = AutoRestartInterval / 300.0f,
|
||||
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
GUITextBlock text = scrollBar.UserData as GUITextBlock;
|
||||
AutoRestartInterval = Math.Max(barScroll * 300.0f, 10.0f);
|
||||
text.Text = autoRestartDelayLabel + ToolBox.SecondsToReadableTime(AutoRestartInterval);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
startIntervalSlider.OnMoved(startIntervalSlider, startIntervalSlider.BarScroll);
|
||||
|
||||
//***********************************************
|
||||
|
||||
var startWhenClientsReady = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
|
||||
TextManager.Get("ServerSettingsStartWhenClientsReady"))
|
||||
{
|
||||
Selected = StartWhenClientsReady,
|
||||
OnSelected = (tickBox) =>
|
||||
{
|
||||
StartWhenClientsReady = tickBox.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CreateLabeledSlider(serverTab, "ServerSettingsStartWhenClientsReadyRatio", out slider, out sliderLabel);
|
||||
string clientsReadyRequiredLabel = sliderLabel.Text;
|
||||
slider.Step = 0.2f;
|
||||
slider.BarScroll = (StartWhenClientsReadyRatio - 0.5f) * 2.0f;
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
StartWhenClientsReadyRatio = barScroll / 2.0f + 0.5f;
|
||||
((GUITextBlock)scrollBar.UserData).Text = clientsReadyRequiredLabel.Replace("[percentage]", ((int)MathUtils.Round(StartWhenClientsReadyRatio * 100.0f, 10.0f)).ToString());
|
||||
return true;
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
//***********************************************
|
||||
|
||||
var allowSpecBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowSpectating"))
|
||||
{
|
||||
Selected = AllowSpectating,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
AllowSpectating = GUITickBox.Selected;
|
||||
GameMain.NetLobbyScreen.LastUpdateID++;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var voteKickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowVoteKick"))
|
||||
{
|
||||
Selected = Voting.AllowVoteKick,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
Voting.AllowVoteKick = !Voting.AllowVoteKick;
|
||||
GameMain.Server.UpdateVoteStatus();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CreateLabeledSlider(serverTab, "ServerSettingsKickVotesRequired", out slider, out sliderLabel);
|
||||
string votesRequiredLabel = sliderLabel.Text;
|
||||
slider.Step = 0.2f;
|
||||
slider.BarScroll = (KickVoteRequiredRatio - 0.5f) * 2.0f;
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
KickVoteRequiredRatio = barScroll / 2.0f + 0.5f;
|
||||
((GUITextBlock)scrollBar.UserData).Text = votesRequiredLabel + (int)MathUtils.Round(KickVoteRequiredRatio * 100.0f, 10.0f) + " %";
|
||||
return true;
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
CreateLabeledSlider(serverTab, "ServerSettingsAutobanTime", out slider, out sliderLabel);
|
||||
string autobanLabel = sliderLabel.Text;
|
||||
slider.Step = 0.05f;
|
||||
slider.BarScroll = AutoBanTime / MaxAutoBanTime;
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
AutoBanTime = Math.Max(barScroll * MaxAutoBanTime, 0);
|
||||
((GUITextBlock)scrollBar.UserData).Text = autobanLabel + ToolBox.SecondsToReadableTime(AutoBanTime);
|
||||
return true;
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
var shareSubsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsShareSubFiles"))
|
||||
{
|
||||
Selected = AllowFileTransfers,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
AllowFileTransfers = GUITickBox.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var randomizeLevelBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsRandomizeSeed"))
|
||||
{
|
||||
Selected = RandomizeSeed,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
RandomizeSeed = GUITickBox.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var saveLogsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsSaveLogs"))
|
||||
{
|
||||
Selected = SaveServerLogs,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
SaveServerLogs = GUITickBox.Selected;
|
||||
showLogButton.Visible = SaveServerLogs;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"))
|
||||
{
|
||||
Selected = AllowRagdollButton,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
AllowRagdollButton = GUITickBox.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var traitorRatioBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsUseTraitorRatio"));
|
||||
|
||||
CreateLabeledSlider(serverTab, "", out slider, out sliderLabel);
|
||||
/*var traitorRatioText = new GUITextBlock(new Rectangle(20, y + 20, 20, 20), "Traitor ratio: 20 %", "", settingsTabs[1], GUI.SmallFont);
|
||||
var traitorRatioSlider = new GUIScrollBar(new Rectangle(150, y + 22, 100, 15), "", 0.1f, settingsTabs[1]);*/
|
||||
var traitorRatioSlider = slider;
|
||||
traitorRatioBox.Selected = TraitorUseRatio;
|
||||
traitorRatioBox.OnSelected = (GUITickBox) =>
|
||||
{
|
||||
TraitorUseRatio = GUITickBox.Selected;
|
||||
traitorRatioSlider.Step = TraitorUseRatio ? 0.01f : 1f / (maxPlayers - 1);
|
||||
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
|
||||
return true;
|
||||
};
|
||||
|
||||
string traitorRatioLabel = TextManager.Get("ServerSettingsTraitorRatio");
|
||||
string traitorCountLabel = TextManager.Get("ServerSettingsTraitorCount");
|
||||
traitorRatioSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
GUITextBlock traitorText = scrollBar.UserData as GUITextBlock;
|
||||
if (TraitorUseRatio)
|
||||
{
|
||||
TraitorRatio = barScroll * 0.9f + 0.1f;
|
||||
traitorText.Text = traitorRatioLabel + (int)MathUtils.Round(TraitorRatio * 100.0f, 1.0f) + " %";
|
||||
}
|
||||
else
|
||||
{
|
||||
TraitorRatio = MathUtils.Round(barScroll * (maxPlayers-1), 1f) + 1;
|
||||
traitorText.Text = traitorCountLabel + TraitorRatio;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
|
||||
traitorRatioBox.OnSelected(traitorRatioBox);
|
||||
|
||||
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsUseKarma"))
|
||||
{
|
||||
Selected = KarmaEnabled,
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
KarmaEnabled = GUITickBox.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// banlist
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
banList.CreateBanFrame(settingsTabs[2]);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// whitelist
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
whitelist.CreateWhiteListFrame(settingsTabs[3]);
|
||||
|
||||
}
|
||||
|
||||
private void CreateLabeledSlider(GUIComponent parent, string labelTag, out GUIScrollBar slider, out GUITextBlock label)
|
||||
{
|
||||
var container = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), parent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
slider = new GUIScrollBar(new RectTransform(new Vector2(0.5f, 0.8f), container.RectTransform), barSize: 0.1f);
|
||||
label = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.8f), container.RectTransform),
|
||||
string.IsNullOrEmpty(labelTag) ? "" : TextManager.Get(labelTag), font: GUI.SmallFont);
|
||||
|
||||
//slider has a reference to the label to change the text when it's used
|
||||
slider.UserData = label;
|
||||
}
|
||||
|
||||
private bool SwitchSubSelection(GUITickBox tickBox)
|
||||
{
|
||||
if (tickBox.Selected)
|
||||
{
|
||||
subSelectionMode = (SelectionMode)tickBox.UserData;
|
||||
Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
|
||||
if (subSelectionMode == SelectionMode.Random)
|
||||
{
|
||||
GameMain.NetLobbyScreen.SubList.Select(Rand.Range(0, GameMain.NetLobbyScreen.SubList.CountChildren));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool SelectSettingsTab(GUIButton button, object obj)
|
||||
{
|
||||
settingsTabIndex = (int)obj;
|
||||
|
||||
for (int i = 0; i < settingsTabs.Length; i++)
|
||||
{
|
||||
settingsTabs[i].Visible = i == settingsTabIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool SwitchModeSelection(GUITickBox tickBox)
|
||||
{
|
||||
if (tickBox.Selected)
|
||||
{
|
||||
modeSelectionMode = (SelectionMode)tickBox.UserData;
|
||||
Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
|
||||
if (modeSelectionMode == SelectionMode.Random)
|
||||
{
|
||||
GameMain.NetLobbyScreen.ModeList.Select(Rand.Range(0, GameMain.NetLobbyScreen.ModeList.CountChildren));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public bool ToggleSettingsFrame(GUIButton button, object obj)
|
||||
{
|
||||
if (settingsFrame == null)
|
||||
{
|
||||
CreateSettingsFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
settingsFrame = null;
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ManagePlayersFrame(GUIFrame infoFrame)
|
||||
{
|
||||
GUIListBox cList = new GUIListBox(new RectTransform(Vector2.One, infoFrame.RectTransform));
|
||||
foreach (Client c in ConnectedClients)
|
||||
{
|
||||
var frame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), cList.Content.RectTransform),
|
||||
c.Name + " (" + c.Connection.RemoteEndPoint.Address.ToString() + ")", style: "ListBoxElement")
|
||||
{
|
||||
Color = (c.InGame && c.Character != null && !c.Character.IsDead) ? Color.Gold * 0.2f : Color.Transparent,
|
||||
HoverColor = Color.LightGray * 0.5f,
|
||||
SelectedColor = Color.Gold * 0.5f
|
||||
};
|
||||
|
||||
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.85f), frame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.05f, 0.0f) },
|
||||
isHorizontal: true);
|
||||
|
||||
var kickButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform),
|
||||
TextManager.Get("Kick"))
|
||||
{
|
||||
UserData = c.Name,
|
||||
OnClicked = GameMain.NetLobbyScreen.KickPlayer
|
||||
};
|
||||
|
||||
var banButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform),
|
||||
TextManager.Get("Ban"))
|
||||
{
|
||||
UserData = c.Name,
|
||||
OnClicked = GameMain.NetLobbyScreen.BanPlayer
|
||||
};
|
||||
|
||||
var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform),
|
||||
TextManager.Get("BanRange"))
|
||||
{
|
||||
UserData = c.Name,
|
||||
OnClicked = GameMain.NetLobbyScreen.BanPlayerRange
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (updateTimer > 0.0f) return;
|
||||
|
||||
for (int i = 0; i<3; i++)
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
|
||||
graphs[i].Update(totalValue[i] / UpdateInterval);
|
||||
@@ -59,9 +59,9 @@ namespace Barotrauma.Networking
|
||||
updateTimer = UpdateInterval;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Rectangle rect, GameServer server)
|
||||
public void Draw(SpriteBatch spriteBatch, Rectangle rect)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, rect, Color.Black*0.4f, true);
|
||||
GUI.DrawRectangle(spriteBatch, rect, Color.Black * 0.4f, true);
|
||||
|
||||
graphs[(int)NetStatType.ReceivedBytes].Draw(spriteBatch, rect, null, 0.0f, Color.Cyan);
|
||||
|
||||
@@ -72,17 +72,17 @@ namespace Barotrauma.Networking
|
||||
GUI.SmallFont.DrawString(spriteBatch,
|
||||
"Peak received: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.ReceivedBytes].LargestValue()) + "/s " +
|
||||
"Avg received: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.ReceivedBytes].Average()) + "/s",
|
||||
new Vector2(rect.X + 10, rect.Y + 10), Color.Cyan);
|
||||
new Vector2(rect.Right + 10, rect.Y + 10), Color.Cyan);
|
||||
|
||||
|
||||
GUI.SmallFont.DrawString(spriteBatch, "Peak sent: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.SentBytes].LargestValue()) + "/s " +
|
||||
"Avg sent: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.SentBytes].Average()) + "/s",
|
||||
new Vector2(rect.X + 10, rect.Y + 30), Color.Orange);
|
||||
new Vector2(rect.Right + 10, rect.Y + 30), Color.Orange);
|
||||
|
||||
GUI.SmallFont.DrawString(spriteBatch, "Peak resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s",
|
||||
new Vector2(rect.X + 10, rect.Y + 50), Color.Red);
|
||||
new Vector2(rect.Right + 10, rect.Y + 50), Color.Red);
|
||||
#if DEBUG
|
||||
int y = 10;
|
||||
/*int y = 10;
|
||||
|
||||
foreach (KeyValuePair<string, long> msgBytesSent in server.messageCount.OrderBy(key => -key.Value))
|
||||
{
|
||||
@@ -91,6 +91,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
y += 15;
|
||||
}
|
||||
|
||||
TODO: reimplement?*/
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
abstract partial class NetworkMember
|
||||
{
|
||||
protected CharacterInfo characterInfo;
|
||||
protected Character myCharacter;
|
||||
|
||||
public CharacterInfo CharacterInfo
|
||||
{
|
||||
get { return characterInfo; }
|
||||
set { characterInfo = value; }
|
||||
}
|
||||
|
||||
public Character Character
|
||||
{
|
||||
get { return myCharacter; }
|
||||
set { myCharacter = value; }
|
||||
}
|
||||
|
||||
protected GUIFrame inGameHUD;
|
||||
protected ChatBox chatBox;
|
||||
protected GUIButton showLogButton;
|
||||
protected GUITickBox cameraFollowsSub;
|
||||
|
||||
private float myCharacterFrameOpenState;
|
||||
|
||||
public GUIFrame InGameHUD
|
||||
{
|
||||
get { return inGameHUD; }
|
||||
}
|
||||
|
||||
public ChatBox ChatBox
|
||||
{
|
||||
get { return chatBox; }
|
||||
}
|
||||
|
||||
private void InitProjSpecific()
|
||||
{
|
||||
inGameHUD = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
cameraFollowsSub = new GUITickBox(new RectTransform(new Vector2(0.05f, 0.05f), inGameHUD.RectTransform, anchor: Anchor.TopCenter)
|
||||
{
|
||||
AbsoluteOffset = new Point(0, 5),
|
||||
MaxSize = new Point(25, 25)
|
||||
}, TextManager.Get("CamFollowSubmarine"))
|
||||
{
|
||||
Selected = Camera.FollowSub,
|
||||
OnSelected = (tbox) =>
|
||||
{
|
||||
Camera.FollowSub = tbox.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
cameraFollowsSub.OnSelected(cameraFollowsSub);
|
||||
|
||||
chatBox = new ChatBox(inGameHUD, isSinglePlayer: false);
|
||||
chatBox.OnEnterMessage += EnterChatMessage;
|
||||
chatBox.InputBox.OnTextChanged += TypingChatMessage;
|
||||
}
|
||||
|
||||
protected void SetRadioButtonColor()
|
||||
{
|
||||
if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f)
|
||||
{
|
||||
chatBox.RadioButton.GetChild<GUIImage>().Color = new Color(60, 60, 60, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
var radioItem = Character.Controlled?.Inventory?.Items.FirstOrDefault(i => i?.GetComponent<WifiComponent>() != null);
|
||||
chatBox.RadioButton.GetChild<GUIImage>().Color =
|
||||
(radioItem != null && Character.Controlled.HasEquippedItem(radioItem) && radioItem.GetComponent<WifiComponent>().CanTransmit()) ?
|
||||
Color.White : new Color(60, 60, 60, 255);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TypingChatMessage(GUITextBox textBox, string text)
|
||||
{
|
||||
return chatBox.TypingChatMessage(textBox, text);
|
||||
}
|
||||
|
||||
public bool EnterChatMessage(GUITextBox textBox, string message)
|
||||
{
|
||||
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
if (textBox == chatBox.InputBox) textBox.Deselect();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == GameMain.Server)
|
||||
{
|
||||
GameMain.Server.SendChatMessage(message, null, null);
|
||||
}
|
||||
else if (this == GameMain.Client)
|
||||
{
|
||||
GameMain.Client.SendChatMessage(message);
|
||||
}
|
||||
|
||||
textBox.Deselect();
|
||||
textBox.Text = "";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void AddToGUIUpdateList()
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
|
||||
if (gameStarted &&
|
||||
Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
inGameHUD.AddToGUIUpdateList();
|
||||
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
GameMain.NetLobbyScreen.MyCharacterFrame.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateHUD(float deltaTime)
|
||||
{
|
||||
GUITextBox msgBox = (Screen.Selected == GameMain.GameScreen ? chatBox.InputBox : GameMain.NetLobbyScreen.TextBox);
|
||||
if (gameStarted && Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
if (!GUI.DisableHUD)
|
||||
{
|
||||
inGameHUD.UpdateManually(deltaTime);
|
||||
chatBox.Update(deltaTime);
|
||||
UpdateFollowSubTickBox();
|
||||
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
myCharacterFrameOpenState = GameMain.NetLobbyScreen.MyCharacterFrameOpen ? myCharacterFrameOpenState + deltaTime * 5 : myCharacterFrameOpenState - deltaTime * 5;
|
||||
myCharacterFrameOpenState = MathHelper.Clamp(myCharacterFrameOpenState, 0.0f, 1.0f);
|
||||
|
||||
var myCharFrame = GameMain.NetLobbyScreen.MyCharacterFrame;
|
||||
int padding = GameMain.GraphicsWidth - myCharFrame.Parent.Rect.Right;
|
||||
|
||||
myCharFrame.RectTransform.AbsoluteOffset =
|
||||
Vector2.SmoothStep(new Vector2(-myCharFrame.Rect.Width - padding, 0.0f), new Vector2(-padding, 0), myCharacterFrameOpenState).ToPoint();
|
||||
}
|
||||
}
|
||||
if (Character.Controlled == null || Character.Controlled.IsDead)
|
||||
{
|
||||
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//tab doesn't autoselect the chatbox when debug console is open,
|
||||
//because tab is used for autocompleting console commands
|
||||
|
||||
if (!DebugConsole.IsOpen && (Screen.Selected != GameMain.GameScreen || msgBox.Visible))
|
||||
{
|
||||
if (PlayerInput.KeyHit(InputType.Chat))
|
||||
{
|
||||
if (msgBox.Selected)
|
||||
{
|
||||
msgBox.Text = "";
|
||||
msgBox.Deselect();
|
||||
}
|
||||
else
|
||||
{
|
||||
msgBox.Select();
|
||||
if ( PlayerInput.KeyHit(InputType.RadioChat))
|
||||
{
|
||||
msgBox.Text = "r; ";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Screen.Selected == GameMain.GameScreen && PlayerInput.KeyHit(InputType.RadioChat) && !msgBox.Selected)
|
||||
{
|
||||
msgBox.Select();
|
||||
msgBox.Text = "r; ";
|
||||
}
|
||||
}
|
||||
|
||||
if (ServerLog.LogFrame != null) ServerLog.LogFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
protected void UpdateFollowSubTickBox()
|
||||
{
|
||||
cameraFollowsSub.Visible = Character.Controlled == null;
|
||||
}
|
||||
|
||||
public virtual void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!gameStarted || Screen.Selected != GameMain.GameScreen || GUI.DisableHUD) return;
|
||||
|
||||
inGameHUD.DrawManually(spriteBatch);
|
||||
|
||||
if (EndVoteCount > 0)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 180.0f, 40),
|
||||
TextManager.Get("EndRoundVotes").Replace("[y]", EndVoteCount.ToString()).Replace("[n]", (EndVoteMax - EndVoteCount).ToString()),
|
||||
Color.White,
|
||||
font: GUI.SmallFont);
|
||||
}
|
||||
|
||||
if (respawnManager != null)
|
||||
{
|
||||
string respawnInfo = "";
|
||||
if (respawnManager.CurrentState == RespawnManager.State.Waiting &&
|
||||
respawnManager.CountdownStarted)
|
||||
{
|
||||
respawnInfo = TextManager.Get(respawnManager.UsingShuttle ? "RespawnShuttleDispatching" : "RespawningIn");
|
||||
respawnInfo = respawnInfo.Replace("[time]", ToolBox.SecondsToReadableTime(respawnManager.RespawnTimer));
|
||||
}
|
||||
else if (respawnManager.CurrentState == RespawnManager.State.Transporting)
|
||||
{
|
||||
respawnInfo = respawnManager.TransportTimer <= 0.0f ?
|
||||
"" :
|
||||
TextManager.Get("RespawnShuttleLeavingIn").Replace("[time]", ToolBox.SecondsToReadableTime(respawnManager.TransportTimer));
|
||||
}
|
||||
|
||||
if (respawnManager != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(120.0f, 10),
|
||||
respawnInfo, Color.White, null, 0, GUI.SmallFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool SelectCrewCharacter(Character character, GUIComponent characterFrame)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CreateKickReasonPrompt(string clientName, bool ban, bool rangeBan = false)
|
||||
{
|
||||
var banReasonPrompt = new GUIMessageBox(
|
||||
TextManager.Get(ban ? "BanReasonPrompt" : "KickReasonPrompt"),
|
||||
"", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") }, 400, 300);
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.6f), banReasonPrompt.InnerFrame.RectTransform, Anchor.Center));
|
||||
var banReasonBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.3f), content.RectTransform))
|
||||
{
|
||||
Wrap = true,
|
||||
MaxTextLength = 100
|
||||
};
|
||||
|
||||
GUINumberInput durationInputDays = null, durationInputHours = null;
|
||||
GUITickBox permaBanTickBox = null;
|
||||
|
||||
if (ban)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.15f), content.RectTransform), TextManager.Get("BanDuration"));
|
||||
permaBanTickBox = new GUITickBox(new RectTransform(new Vector2(0.8f, 0.15f), content.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.0f) },
|
||||
TextManager.Get("BanPermanent"))
|
||||
{
|
||||
Selected = true
|
||||
};
|
||||
|
||||
var durationContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.15f), content.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Visible = false
|
||||
};
|
||||
|
||||
permaBanTickBox.OnSelected += (tickBox) =>
|
||||
{
|
||||
durationContainer.Visible = !tickBox.Selected;
|
||||
return true;
|
||||
};
|
||||
|
||||
durationInputDays = new GUINumberInput(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), GUINumberInput.NumberType.Int)
|
||||
{
|
||||
MinValueInt = 0,
|
||||
MaxValueFloat = 1000
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), TextManager.Get("Days"));
|
||||
durationInputHours = new GUINumberInput(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), GUINumberInput.NumberType.Int)
|
||||
{
|
||||
MinValueInt = 0,
|
||||
MaxValueFloat = 24
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), TextManager.Get("Hours"));
|
||||
}
|
||||
|
||||
banReasonPrompt.Buttons[0].OnClicked += (btn, userData) =>
|
||||
{
|
||||
if (ban)
|
||||
{
|
||||
if (!permaBanTickBox.Selected)
|
||||
{
|
||||
TimeSpan banDuration = new TimeSpan(durationInputDays.IntValue, durationInputHours.IntValue, 0, 0);
|
||||
BanPlayer(clientName, banReasonBox.Text, ban, banDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
BanPlayer(clientName, banReasonBox.Text, ban);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
KickPlayer(clientName, banReasonBox.Text);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
banReasonPrompt.Buttons[0].OnClicked += banReasonPrompt.Close;
|
||||
banReasonPrompt.Buttons[1].OnClicked += banReasonPrompt.Close;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class RespawnManager
|
||||
{
|
||||
partial void UpdateWaiting(float deltaTime)
|
||||
{
|
||||
if (CountdownStarted)
|
||||
{
|
||||
respawnTimer = Math.Max(0.0f, respawnTimer - deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,60 @@ using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class ServerInfo
|
||||
class ServerInfo
|
||||
{
|
||||
public string IP;
|
||||
public string Port;
|
||||
public string ServerName;
|
||||
public string ServerMessage;
|
||||
public bool GameStarted;
|
||||
public int PlayerCount;
|
||||
public int MaxPlayers;
|
||||
public bool HasPassword;
|
||||
|
||||
public bool PingChecked;
|
||||
public int Ping = -1;
|
||||
|
||||
//null value means that the value isn't known (the server may be using
|
||||
//an old version of the game that didn't report these values or the FetchRules query to Steam may not have finished yet)
|
||||
public bool? UsingWhiteList;
|
||||
public SelectionMode? ModeSelectionMode;
|
||||
public SelectionMode? SubSelectionMode;
|
||||
public bool? AllowSpectating;
|
||||
public bool? AllowRespawn;
|
||||
public YesNoMaybe? TraitorsEnabled;
|
||||
public string GameMode;
|
||||
|
||||
public bool? RespondedToSteamQuery = null;
|
||||
|
||||
public string GameVersion;
|
||||
public List<string> ContentPackageNames
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new List<string>();
|
||||
public List<string> ContentPackageHashes
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new List<string>();
|
||||
public List<string> ContentPackageWorkshopUrls
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new List<string>();
|
||||
|
||||
public bool ContentPackagesMatch(IEnumerable<ContentPackage> myContentPackages)
|
||||
{
|
||||
return ContentPackagesMatch(myContentPackages.Select(cp => cp.MD5hash.Hash));
|
||||
}
|
||||
|
||||
public bool ContentPackagesMatch(IEnumerable<string> myContentPackageHashes)
|
||||
{
|
||||
HashSet<string> contentPackageHashes = new HashSet<string>(ContentPackageHashes);
|
||||
return contentPackageHashes.SetEquals(myContentPackageHashes);
|
||||
}
|
||||
|
||||
public void CreatePreviewWindow(GUIMessageBox messageBox)
|
||||
{
|
||||
var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), messageBox.Content.RectTransform), ServerName, textAlignment: Alignment.Center, font: GUI.LargeFont, wrap: true);
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Barotrauma.Networking
|
||||
public GUIButton LogFrame;
|
||||
private GUIListBox listBox;
|
||||
|
||||
private string msgFilter;
|
||||
|
||||
public void CreateLogFrame()
|
||||
{
|
||||
LogFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker")
|
||||
@@ -24,7 +26,7 @@ namespace Barotrauma.Networking
|
||||
GUIFrame innerFrame = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.4f), LogFrame.RectTransform, Anchor.Center) { MinSize = new Point(600, 420) });
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.85f), innerFrame.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, -0.03f) }, style: null);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.05f), paddedFrame.RectTransform, Anchor.TopRight), "Filter", font: GUI.SmallFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.05f), paddedFrame.RectTransform, Anchor.TopRight), TextManager.Get("ServerLog.Filter"), font: GUI.SmallFont);
|
||||
GUITextBox searchBox = new GUITextBox(new RectTransform(new Vector2(0.6f, 0.05f), paddedFrame.RectTransform, Anchor.TopRight), font: GUI.SmallFont);
|
||||
searchBox.OnTextChanged += (textBox, text) =>
|
||||
{
|
||||
@@ -47,7 +49,7 @@ namespace Barotrauma.Networking
|
||||
int y = 30;
|
||||
foreach (MessageType msgType in Enum.GetValues(typeof(MessageType)))
|
||||
{
|
||||
var tickBox = new GUITickBox(new RectTransform(new Point(20, 20), tickBoxContainer.RectTransform), messageTypeName[(int)msgType], font: GUI.SmallFont)
|
||||
var tickBox = new GUITickBox(new RectTransform(new Point(20, 20), tickBoxContainer.RectTransform), TextManager.Get("ServerLog." + messageTypeName[(int)msgType]), font: GUI.SmallFont)
|
||||
{
|
||||
Selected = true,
|
||||
TextColor = messageColor[(int)msgType]
|
||||
@@ -77,7 +79,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (listBox.BarScroll == 0.0f || listBox.BarScroll == 1.0f) listBox.BarScroll = 1.0f;
|
||||
|
||||
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.05f), innerFrame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.02f, 0.03f) }, "Close");
|
||||
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.05f), innerFrame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.02f, 0.03f) }, TextManager.Get("Close"));
|
||||
closeButton.OnClicked = (button, userData) =>
|
||||
{
|
||||
LogFrame = null;
|
||||
|
||||
@@ -0,0 +1,879 @@
|
||||
using Barotrauma.Networking;
|
||||
using Facepunch.Steamworks;
|
||||
using RestSharp.Extensions.MonoHttp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace Barotrauma.Steam
|
||||
{
|
||||
partial class SteamManager
|
||||
{
|
||||
private SteamManager()
|
||||
{
|
||||
try
|
||||
{
|
||||
client = new Facepunch.Steamworks.Client(AppID);
|
||||
isInitialized = client.IsSubscribed && client.IsValid;
|
||||
|
||||
if (isInitialized)
|
||||
{
|
||||
DebugConsole.Log("Logged in as " + client.Username + " (SteamID " + client.SteamId + ")");
|
||||
}
|
||||
}
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
isInitialized = false;
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("SteamDllNotFound"));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
isInitialized = false;
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("SteamClientInitFailed"));
|
||||
}
|
||||
|
||||
if (!isInitialized)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
Facepunch.Steamworks.Client.Instance.Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (GameSettings.VerboseLogging) DebugConsole.ThrowError("Disposing Steam client failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ulong GetSteamID()
|
||||
{
|
||||
if (instance == null || !instance.isInitialized)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return instance.client.SteamId;
|
||||
}
|
||||
|
||||
public static ulong GetWorkshopItemIDFromUrl(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(url);
|
||||
string idStr = HttpUtility.ParseQueryString(uri.Query).Get("id");
|
||||
if (ulong.TryParse(idStr, out ulong id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to get Workshop item ID from the url \"" + url + "\"!", e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#region Connecting to servers
|
||||
|
||||
public static bool GetServers(Action<Networking.ServerInfo> onServerFound, Action<Networking.ServerInfo> onServerRulesReceived, Action onFinished)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var filter = new ServerList.Filter
|
||||
{
|
||||
{ "appid", AppID.ToString() },
|
||||
{ "gamedir", "Barotrauma" },
|
||||
{ "secure", "1" }
|
||||
};
|
||||
|
||||
//include unresponsive servers in the server list
|
||||
|
||||
//the response is queried using the server's query port, not the game port,
|
||||
//so it may be possible to play on the server even if it doesn't respond to server list queries
|
||||
var query = instance.client.ServerList.Internet(filter);
|
||||
query.OnUpdate += () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
|
||||
query.OnFinished = onFinished;
|
||||
|
||||
var localQuery = instance.client.ServerList.Local(filter);
|
||||
localQuery.OnUpdate += () => { UpdateServerQuery(localQuery, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
|
||||
localQuery.OnFinished = onFinished;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void UpdateServerQuery(ServerList.Request query, Action<Networking.ServerInfo> onServerFound, Action<Networking.ServerInfo> onServerRulesReceived, bool includeUnresponsive)
|
||||
{
|
||||
IEnumerable<ServerList.Server> servers = includeUnresponsive ?
|
||||
new List<ServerList.Server>(query.Responded).Concat(query.Unresponsive) :
|
||||
query.Responded;
|
||||
|
||||
foreach (ServerList.Server s in servers)
|
||||
{
|
||||
if (!ValidateServerInfo(s)) { continue; }
|
||||
|
||||
bool responded = query.Responded.Contains(s);
|
||||
if (responded)
|
||||
{
|
||||
DebugConsole.Log(s.Name + " responded to server query.");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.Log(s.Name + " did not respond to server query.");
|
||||
}
|
||||
var serverInfo = new ServerInfo()
|
||||
{
|
||||
ServerName = s.Name,
|
||||
Port = s.ConnectionPort.ToString(),
|
||||
IP = s.Address.ToString(),
|
||||
PlayerCount = s.Players,
|
||||
MaxPlayers = s.MaxPlayers,
|
||||
HasPassword = s.Passworded,
|
||||
RespondedToSteamQuery = responded
|
||||
};
|
||||
serverInfo.PingChecked = true;
|
||||
serverInfo.Ping = s.Ping;
|
||||
if (responded)
|
||||
{
|
||||
s.FetchRules();
|
||||
}
|
||||
s.OnReceivedRules += (bool rulesReceived) =>
|
||||
{
|
||||
if (!rulesReceived || s.Rules == null) { return; }
|
||||
|
||||
if (s.Rules.ContainsKey("message")) serverInfo.ServerMessage = s.Rules["message"];
|
||||
if (s.Rules.ContainsKey("version")) serverInfo.GameVersion = s.Rules["version"];
|
||||
|
||||
if (s.Rules.ContainsKey("contentpackage")) serverInfo.ContentPackageNames.AddRange(s.Rules["contentpackage"].Split(','));
|
||||
if (s.Rules.ContainsKey("contentpackagehash")) serverInfo.ContentPackageHashes.AddRange(s.Rules["contentpackagehash"].Split(','));
|
||||
if (s.Rules.ContainsKey("contentpackageurl")) serverInfo.ContentPackageWorkshopUrls.AddRange(s.Rules["contentpackageurl"].Split(','));
|
||||
|
||||
if (s.Rules.ContainsKey("usingwhitelist")) serverInfo.UsingWhiteList = s.Rules["usingwhitelist"] == "True";
|
||||
if (s.Rules.ContainsKey("modeselectionmode"))
|
||||
{
|
||||
if (Enum.TryParse(s.Rules["modeselectionmode"], out SelectionMode selectionMode)) serverInfo.ModeSelectionMode = selectionMode;
|
||||
}
|
||||
if (s.Rules.ContainsKey("subselectionmode"))
|
||||
{
|
||||
if (Enum.TryParse(s.Rules["subselectionmode"], out SelectionMode selectionMode)) serverInfo.SubSelectionMode = selectionMode;
|
||||
}
|
||||
if (s.Rules.ContainsKey("allowspectating")) serverInfo.AllowSpectating = s.Rules["allowspectating"] == "True";
|
||||
if (s.Rules.ContainsKey("allowrespawn")) serverInfo.AllowRespawn = s.Rules["allowrespawn"] == "True";
|
||||
if (s.Rules.ContainsKey("traitors"))
|
||||
{
|
||||
if (Enum.TryParse(s.Rules["traitors"], out YesNoMaybe traitorsEnabled)) serverInfo.TraitorsEnabled = traitorsEnabled;
|
||||
}
|
||||
|
||||
if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count ||
|
||||
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count)
|
||||
{
|
||||
//invalid contentpackage info
|
||||
serverInfo.ContentPackageNames.Clear();
|
||||
serverInfo.ContentPackageHashes.Clear();
|
||||
}
|
||||
onServerRulesReceived(serverInfo);
|
||||
};
|
||||
|
||||
onServerFound(serverInfo);
|
||||
}
|
||||
query.Responded.Clear();
|
||||
}
|
||||
|
||||
private static bool ValidateServerInfo(ServerList.Server server)
|
||||
{
|
||||
if (string.IsNullOrEmpty(server.Name)) { return false; }
|
||||
if (server.Address == null) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Auth.Ticket GetAuthSessionTicket()
|
||||
{
|
||||
if (instance == null || !instance.isInitialized)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return instance.client.Auth.GetAuthSessionTicket();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Workshop
|
||||
|
||||
public const string WorkshopItemStagingFolder = "NewWorkshopItem";
|
||||
public const string WorkshopItemPreviewImageFolder = "Workshop";
|
||||
public const string PreviewImageName = "PreviewImage.png";
|
||||
private const string MetadataFileName = "filelist.xml";
|
||||
private const string DefaultPreviewImagePath = "Content/DefaultWorkshopPreviewImage.png";
|
||||
|
||||
private Sprite defaultPreviewImage;
|
||||
public Sprite DefaultPreviewImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (defaultPreviewImage == null)
|
||||
{
|
||||
defaultPreviewImage = new Sprite(DefaultPreviewImagePath, sourceRectangle: null);
|
||||
}
|
||||
return defaultPreviewImage;
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetSubscribedWorkshopItems(Action<IList<Workshop.Item>> onItemsFound, List<string> requireTags = null)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized) return;
|
||||
|
||||
var query = instance.client.Workshop.CreateQuery();
|
||||
query.Order = Workshop.Order.RankedByTrend;
|
||||
query.UserId = instance.client.SteamId;
|
||||
query.UserQueryType = Workshop.UserQueryType.Subscribed;
|
||||
query.UploaderAppId = AppID;
|
||||
if (requireTags != null) query.RequireTags = requireTags;
|
||||
query.Run();
|
||||
query.OnResult += (Workshop.Query q) =>
|
||||
{
|
||||
onItemsFound?.Invoke(q.Items);
|
||||
};
|
||||
}
|
||||
|
||||
public static void GetPopularWorkshopItems(Action<IList<Workshop.Item>> onItemsFound, int amount, List<string> requireTags = null)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized) return;
|
||||
|
||||
var query = instance.client.Workshop.CreateQuery();
|
||||
query.Order = Workshop.Order.RankedByTrend;
|
||||
query.RankedByTrendDays = 30;
|
||||
query.UploaderAppId = AppID;
|
||||
if (requireTags != null) query.RequireTags = requireTags;
|
||||
query.Run();
|
||||
query.OnResult += (Workshop.Query q) =>
|
||||
{
|
||||
//count the number of each unique tag
|
||||
foreach (var item in q.Items)
|
||||
{
|
||||
foreach (string tag in item.Tags)
|
||||
{
|
||||
if (string.IsNullOrEmpty(tag)) { continue; }
|
||||
string caseInvariantTag = tag.ToLowerInvariant();
|
||||
if (!instance.tagCommonness.ContainsKey(caseInvariantTag))
|
||||
{
|
||||
instance.tagCommonness[caseInvariantTag] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
instance.tagCommonness[caseInvariantTag]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
//populate the popularTags list with tags sorted by commonness
|
||||
instance.popularTags.Clear();
|
||||
foreach (KeyValuePair<string, int> tagCommonness in instance.tagCommonness)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < instance.popularTags.Count &&
|
||||
instance.tagCommonness[instance.popularTags[i]] > tagCommonness.Value)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
instance.popularTags.Insert(i, tagCommonness.Key);
|
||||
}
|
||||
|
||||
var nonSubscribedItems = q.Items.Where(it => !it.Subscribed && !it.Installed);
|
||||
if (nonSubscribedItems.Count() > amount)
|
||||
{
|
||||
nonSubscribedItems = nonSubscribedItems.Take(amount);
|
||||
}
|
||||
onItemsFound?.Invoke(nonSubscribedItems.ToList());
|
||||
};
|
||||
}
|
||||
|
||||
public static void GetPublishedWorkshopItems(Action<IList<Workshop.Item>> onItemsFound, List<string> requireTags = null)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized) return;
|
||||
|
||||
var query = instance.client.Workshop.CreateQuery();
|
||||
query.Order = Workshop.Order.RankedByPublicationDate;
|
||||
query.UserId = instance.client.SteamId;
|
||||
query.UserQueryType = Workshop.UserQueryType.Published;
|
||||
query.UploaderAppId = AppID;
|
||||
if (requireTags != null) query.RequireTags = requireTags;
|
||||
query.Run();
|
||||
query.OnResult += (Workshop.Query q) =>
|
||||
{
|
||||
onItemsFound?.Invoke(q.Items);
|
||||
};
|
||||
}
|
||||
|
||||
public static void SubscribeToWorkshopItem(string itemUrl)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized) return;
|
||||
|
||||
ulong id = GetWorkshopItemIDFromUrl(itemUrl);
|
||||
if (id == 0) { return; }
|
||||
|
||||
var item = instance.client.Workshop.GetItem(id);
|
||||
if (item == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to find a Steam Workshop item with the ID " + id + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
item.Subscribe();
|
||||
item.Download();
|
||||
}
|
||||
|
||||
public static void SaveToWorkshop(Submarine sub)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized) return;
|
||||
|
||||
Workshop.Editor item;
|
||||
ContentPackage contentPackage;
|
||||
try
|
||||
{
|
||||
CreateWorkshopItemStaging(
|
||||
new List<ContentFile>() { new ContentFile(sub.FilePath, ContentType.None) },
|
||||
out item, out contentPackage);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Creating the workshop item failed.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
item.Description = sub.Description;
|
||||
item.Title = sub.Name;
|
||||
item.Tags.Add("Submarine");
|
||||
|
||||
string subPreviewPath = Path.GetFullPath(Path.Combine(item.Folder, PreviewImageName));
|
||||
try
|
||||
{
|
||||
using (Stream s = File.Create(subPreviewPath))
|
||||
{
|
||||
sub.PreviewImage.Texture.SaveAsPng(s, (int)sub.PreviewImage.size.X, (int)sub.PreviewImage.size.Y);
|
||||
item.PreviewImage = subPreviewPath;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Saving submarine preview image failed.", e);
|
||||
item.PreviewImage = null;
|
||||
}
|
||||
StartPublishItem(contentPackage, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new folder, copies the specified files there and creates a metadata file with install instructions.
|
||||
/// </summary>
|
||||
public static void CreateWorkshopItemStaging(List<ContentFile> contentFiles, out Workshop.Editor itemEditor, out ContentPackage contentPackage)
|
||||
{
|
||||
var stagingFolder = new DirectoryInfo(WorkshopItemStagingFolder);
|
||||
if (stagingFolder.Exists)
|
||||
{
|
||||
SaveUtil.ClearFolder(stagingFolder.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
stagingFolder.Create();
|
||||
}
|
||||
Directory.CreateDirectory(Path.Combine(WorkshopItemStagingFolder, "Submarines"));
|
||||
Directory.CreateDirectory(Path.Combine(WorkshopItemStagingFolder, "Mods"));
|
||||
Directory.CreateDirectory(Path.Combine(WorkshopItemStagingFolder, "Mods", "ModName"));
|
||||
|
||||
itemEditor = instance.client.Workshop.CreateItem(Workshop.ItemType.Community);
|
||||
itemEditor.Visibility = Workshop.Editor.VisibilityType.Public;
|
||||
itemEditor.WorkshopUploadAppId = AppID;
|
||||
itemEditor.Folder = stagingFolder.FullName;
|
||||
|
||||
string previewImagePath = Path.GetFullPath(Path.Combine(itemEditor.Folder, PreviewImageName));
|
||||
File.Copy("Content/DefaultWorkshopPreviewImage.png", previewImagePath);
|
||||
|
||||
//copy content files to the staging folder
|
||||
List<string> copiedFilePaths = new List<string>();
|
||||
foreach (ContentFile file in contentFiles)
|
||||
{
|
||||
string relativePath = UpdaterUtil.GetRelativePath(Path.GetFullPath(file.Path), Environment.CurrentDirectory);
|
||||
string destinationPath = Path.Combine(stagingFolder.FullName, relativePath);
|
||||
//make sure the directory exists
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
|
||||
File.Copy(file.Path, destinationPath);
|
||||
copiedFilePaths.Add(destinationPath);
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(copiedFilePaths.Count == contentFiles.Count);
|
||||
|
||||
//create a new content package and include the copied files in it
|
||||
contentPackage = ContentPackage.CreatePackage("ContentPackage", Path.Combine(itemEditor.Folder, MetadataFileName), false);
|
||||
for (int i = 0; i < copiedFilePaths.Count; i++)
|
||||
{
|
||||
contentPackage.AddFile(copiedFilePaths[i], contentFiles[i].Type);
|
||||
}
|
||||
|
||||
contentPackage.Save(Path.Combine(stagingFolder.FullName, MetadataFileName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of the specified workshop item in the staging folder and an editor that can be used to edit and update the item
|
||||
/// </summary>
|
||||
public static void CreateWorkshopItemStaging(Workshop.Item existingItem, out Workshop.Editor itemEditor, out ContentPackage contentPackage)
|
||||
{
|
||||
if (!existingItem.Installed)
|
||||
{
|
||||
itemEditor = null;
|
||||
contentPackage = null;
|
||||
DebugConsole.ThrowError("Cannot edit the workshop item \"" + existingItem.Title + "\" because it has not been installed.");
|
||||
return;
|
||||
}
|
||||
|
||||
var stagingFolder = new DirectoryInfo(WorkshopItemStagingFolder);
|
||||
if (stagingFolder.Exists)
|
||||
{
|
||||
SaveUtil.ClearFolder(stagingFolder.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
stagingFolder.Create();
|
||||
}
|
||||
|
||||
itemEditor = instance.client.Workshop.EditItem(existingItem.Id);
|
||||
itemEditor.Visibility = Workshop.Editor.VisibilityType.Public;
|
||||
itemEditor.Title = existingItem.Title;
|
||||
itemEditor.Tags = existingItem.Tags.ToList();
|
||||
itemEditor.Description = existingItem.Description;
|
||||
itemEditor.WorkshopUploadAppId = AppID;
|
||||
itemEditor.Folder = stagingFolder.FullName;
|
||||
|
||||
string previewImagePath = Path.GetFullPath(Path.Combine(itemEditor.Folder, PreviewImageName));
|
||||
itemEditor.PreviewImage = previewImagePath;
|
||||
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
if (File.Exists(previewImagePath))
|
||||
{
|
||||
File.Delete(previewImagePath);
|
||||
}
|
||||
client.DownloadFile(new Uri(existingItem.PreviewImageUrl), previewImagePath);
|
||||
}
|
||||
|
||||
ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem.Directory.FullName, MetadataFileName));
|
||||
//item already installed, copy it from the game folder
|
||||
if (existingItem != null && CheckWorkshopItemEnabled(existingItem, checkContentFiles: false))
|
||||
{
|
||||
string installedItemPath = GetWorkshopItemContentPackagePath(tempContentPackage);
|
||||
if (File.Exists(installedItemPath))
|
||||
{
|
||||
tempContentPackage = new ContentPackage(installedItemPath);
|
||||
}
|
||||
}
|
||||
if (File.Exists(tempContentPackage.Path))
|
||||
{
|
||||
string newContentPackagePath = Path.Combine(WorkshopItemStagingFolder, MetadataFileName);
|
||||
File.Copy(tempContentPackage.Path, newContentPackagePath, overwrite: true);
|
||||
contentPackage = new ContentPackage(newContentPackagePath);
|
||||
foreach (ContentFile contentFile in tempContentPackage.Files)
|
||||
{
|
||||
string sourceFile;
|
||||
if (contentFile.Type == ContentType.Submarine && File.Exists(contentFile.Path))
|
||||
{
|
||||
sourceFile = contentFile.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceFile = Path.Combine(existingItem.Directory.FullName, contentFile.Path);
|
||||
}
|
||||
if (!File.Exists(sourceFile)) { continue; }
|
||||
//make sure the destination directory exists
|
||||
string destinationPath = Path.Combine(WorkshopItemStagingFolder, contentFile.Path);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
|
||||
File.Copy(sourceFile, destinationPath, overwrite: true);
|
||||
contentPackage.AddFile(contentFile.Path, contentFile.Type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
contentPackage = ContentPackage.CreatePackage(existingItem.Title, Path.Combine(WorkshopItemStagingFolder, MetadataFileName), false);
|
||||
contentPackage.Save(contentPackage.Path);
|
||||
}
|
||||
}
|
||||
|
||||
public static void StartPublishItem(ContentPackage contentPackage, Workshop.Editor item)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized) return;
|
||||
|
||||
if (string.IsNullOrEmpty(item.Title))
|
||||
{
|
||||
DebugConsole.ThrowError("Cannot publish workshop item - title not set.");
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(item.Folder))
|
||||
{
|
||||
DebugConsole.ThrowError("Cannot publish workshop item \"" + item.Title + "\" - folder not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
contentPackage.Name = item.Title;
|
||||
contentPackage.GameVersion = GameMain.Version;
|
||||
contentPackage.Save(Path.Combine(WorkshopItemStagingFolder, MetadataFileName));
|
||||
|
||||
if (File.Exists(PreviewImageName)) File.Delete(PreviewImageName);
|
||||
//move the preview image out of the staging folder, it does not need to be included in the folder sent to Workshop
|
||||
File.Move(Path.GetFullPath(Path.Combine(item.Folder, PreviewImageName)), PreviewImageName);
|
||||
item.PreviewImage = Path.GetFullPath(PreviewImageName);
|
||||
|
||||
CoroutineManager.StartCoroutine(PublishItem(item));
|
||||
}
|
||||
|
||||
private static IEnumerable<object> PublishItem(Workshop.Editor item)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
item.Publish();
|
||||
while (item.Publishing)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(item.Error))
|
||||
{
|
||||
DebugConsole.NewMessage("Published workshop item " + item.Title + " successfully.", Microsoft.Xna.Framework.Color.LightGreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Publishing workshop item " + item.Title + " failed. " + item.Error);
|
||||
}
|
||||
|
||||
SaveUtil.ClearFolder(WorkshopItemStagingFolder);
|
||||
Directory.Delete(WorkshopItemStagingFolder);
|
||||
File.Delete(PreviewImageName);
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables a workshop item by moving it to the game folder.
|
||||
/// </summary>
|
||||
public static bool EnableWorkShopItem(Workshop.Item item, bool allowFileOverwrite, out string errorMsg)
|
||||
{
|
||||
if (!item.Installed)
|
||||
{
|
||||
errorMsg = TextManager.Get("WorkshopErrorInstallRequiredToEnable").Replace("[itemname]", item.Title);
|
||||
DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentPackage contentPackage = new ContentPackage(Path.Combine(item.Directory.FullName, MetadataFileName));
|
||||
string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage);
|
||||
|
||||
var allPackageFiles = Directory.GetFiles(item.Directory.FullName, "*", SearchOption.AllDirectories);
|
||||
List<string> nonContentFiles = new List<string>();
|
||||
foreach (string file in allPackageFiles)
|
||||
{
|
||||
if (file == MetadataFileName) { continue; }
|
||||
string relativePath = UpdaterUtil.GetRelativePath(file, item.Directory.FullName);
|
||||
string fullPath = Path.GetFullPath(relativePath);
|
||||
if (contentPackage.Files.Any(f => { string fp = Path.GetFullPath(f.Path); return fp == fullPath; })) { continue; }
|
||||
if (ContentPackage.IsModFilePathAllowed(relativePath))
|
||||
{
|
||||
nonContentFiles.Add(relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowFileOverwrite)
|
||||
{
|
||||
if (File.Exists(newContentPackagePath))
|
||||
{
|
||||
errorMsg = TextManager.Get("WorkshopErrorOverwriteOnEnable")
|
||||
.Replace("[itemname]", item.Title)
|
||||
.Replace("[filename]", newContentPackagePath);
|
||||
DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (ContentFile contentFile in contentPackage.Files)
|
||||
{
|
||||
string sourceFile = Path.Combine(item.Directory.FullName, contentFile.Path);
|
||||
if (File.Exists(sourceFile) && File.Exists(contentFile.Path))
|
||||
{
|
||||
errorMsg = TextManager.Get("WorkshopErrorOverwriteOnEnable")
|
||||
.Replace("[itemname]", item.Title)
|
||||
.Replace("[filename]", contentFile.Path);
|
||||
DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
//we only need to create a new content package for the item if it contains content with a type other than None or Submarine
|
||||
//e.g. items that are just a sub file are just copied to the game folder
|
||||
if (contentPackage.Files.Any(f => f.Type != ContentType.None && f.Type != ContentType.Submarine))
|
||||
{
|
||||
File.Copy(contentPackage.Path, newContentPackagePath);
|
||||
}
|
||||
|
||||
foreach (ContentFile contentFile in contentPackage.Files)
|
||||
{
|
||||
string sourceFile = Path.Combine(item.Directory.FullName, contentFile.Path);
|
||||
if (!File.Exists(sourceFile)) { continue; }
|
||||
if (!ContentPackage.IsModFilePathAllowed(contentFile))
|
||||
{
|
||||
DebugConsole.ThrowError(TextManager.Get("WorkshopErrorIllegalPathOnEnable").Replace("[filename]", contentFile.Path));
|
||||
continue;
|
||||
}
|
||||
|
||||
//make sure the destination directory exists
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(contentFile.Path));
|
||||
File.Copy(sourceFile, contentFile.Path, overwrite: true);
|
||||
}
|
||||
|
||||
foreach (string nonContentFile in nonContentFiles)
|
||||
{
|
||||
string sourceFile = Path.Combine(item.Directory.FullName, nonContentFile);
|
||||
if (!File.Exists(sourceFile)) { continue; }
|
||||
if (!ContentPackage.IsModFilePathAllowed(nonContentFile))
|
||||
{
|
||||
DebugConsole.ThrowError(TextManager.Get("WorkshopErrorIllegalPathOnEnable").Replace("[filename]", nonContentFile));
|
||||
continue;
|
||||
}
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(nonContentFile));
|
||||
File.Copy(sourceFile, nonContentFile, overwrite: true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorMsg = TextManager.Get("WorkshopErrorEnableFailed").Replace("[itemname]", item.Title) + " " + e.Message;
|
||||
DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
var newPackage = new ContentPackage(contentPackage.Path, newContentPackagePath)
|
||||
{
|
||||
SteamWorkshopUrl = item.Url,
|
||||
InstallTime = item.Modified > item.Created ? item.Modified : item.Created
|
||||
};
|
||||
newPackage.Save(newContentPackagePath);
|
||||
ContentPackage.List.Add(newPackage);
|
||||
if (newPackage.CorePackage)
|
||||
{
|
||||
//if enabling a core package, disable all other core packages
|
||||
GameMain.Config.SelectedContentPackages.RemoveWhere(cp => cp.CorePackage);
|
||||
}
|
||||
GameMain.Config.SelectedContentPackages.Add(newPackage);
|
||||
GameMain.Config.SaveNewPlayerConfig();
|
||||
|
||||
if (newPackage.Files.Any(f => f.Type == ContentType.Submarine))
|
||||
{
|
||||
Submarine.RefreshSavedSubs();
|
||||
}
|
||||
|
||||
errorMsg = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables a workshop item by removing the files from the game folder.
|
||||
/// </summary>
|
||||
public static bool DisableWorkShopItem(Workshop.Item item, out string errorMsg)
|
||||
{
|
||||
if (!item.Installed)
|
||||
{
|
||||
errorMsg = "Cannot disable workshop item \"" + item.Title + "\" because it has not been installed.";
|
||||
DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentPackage contentPackage = new ContentPackage(Path.Combine(item.Directory.FullName, MetadataFileName));
|
||||
string installedContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage);
|
||||
|
||||
var allPackageFiles = Directory.GetFiles(item.Directory.FullName, "*", SearchOption.AllDirectories);
|
||||
List<string> nonContentFiles = new List<string>();
|
||||
foreach (string file in allPackageFiles)
|
||||
{
|
||||
if (file == MetadataFileName) { continue; }
|
||||
string relativePath = UpdaterUtil.GetRelativePath(file, item.Directory.FullName);
|
||||
string fullPath = Path.GetFullPath(relativePath);
|
||||
if (contentPackage.Files.Any(f => { string fp = Path.GetFullPath(f.Path); return fp == fullPath; })) { continue; }
|
||||
if (ContentPackage.IsModFilePathAllowed(relativePath))
|
||||
{
|
||||
nonContentFiles.Add(relativePath);
|
||||
}
|
||||
}
|
||||
if (File.Exists(installedContentPackagePath)) { File.Delete(installedContentPackagePath); }
|
||||
|
||||
bool wasSub = contentPackage.Files.Any(f => f.Type == ContentType.Submarine);
|
||||
|
||||
HashSet<string> directories = new HashSet<string>();
|
||||
try
|
||||
{
|
||||
foreach (ContentFile contentFile in contentPackage.Files)
|
||||
{
|
||||
if (!ContentPackage.IsModFilePathAllowed(contentFile))
|
||||
{
|
||||
//Workshop items are not allowed to add or modify files in the Content or Data folders;
|
||||
continue;
|
||||
}
|
||||
if (!File.Exists(contentFile.Path)) { continue; }
|
||||
File.Delete(contentFile.Path);
|
||||
directories.Add(Path.GetDirectoryName(contentFile.Path));
|
||||
}
|
||||
foreach (string nonContentFile in nonContentFiles)
|
||||
{
|
||||
if (!ContentPackage.IsModFilePathAllowed(nonContentFile))
|
||||
{
|
||||
//Workshop items are not allowed to add or modify files in the Content or Data folders;
|
||||
continue;
|
||||
}
|
||||
if (!File.Exists(nonContentFile)) { continue; }
|
||||
File.Delete(nonContentFile);
|
||||
directories.Add(Path.GetDirectoryName(nonContentFile));
|
||||
}
|
||||
|
||||
foreach (string directory in directories)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory)) { continue; }
|
||||
if (Directory.GetFiles(directory, "*", SearchOption.AllDirectories).Count() == 0)
|
||||
{
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
ContentPackage.List.RemoveAll(cp => System.IO.Path.GetFullPath(cp.Path) == System.IO.Path.GetFullPath(installedContentPackagePath));
|
||||
GameMain.Config.SelectedContentPackages.RemoveWhere(cp => !ContentPackage.List.Contains(cp));
|
||||
GameMain.Config.SaveNewPlayerConfig();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorMsg = "Disabling the workshop item \"" + item.Title + "\" failed. " + e.Message;
|
||||
DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wasSub)
|
||||
{
|
||||
Submarine.RefreshSavedSubs();
|
||||
}
|
||||
|
||||
errorMsg = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the item compatible with this version of Barotrauma. Returns null if compatibility couldn't be determined (item not installed)
|
||||
/// </summary>
|
||||
public static bool? CheckWorkshopItemCompatibility(Workshop.Item item)
|
||||
{
|
||||
if (!item.Installed) { return null; }
|
||||
|
||||
string metaDataPath = Path.Combine(item.Directory.FullName, MetadataFileName);
|
||||
if (!File.Exists(metaDataPath))
|
||||
{
|
||||
throw new FileNotFoundException("Metadata file for the Workshop item \"" + item.Title + "\" not found. The file may be corrupted.");
|
||||
}
|
||||
|
||||
ContentPackage contentPackage = new ContentPackage(metaDataPath);
|
||||
return contentPackage.IsCompatible();
|
||||
}
|
||||
|
||||
public static bool CheckWorkshopItemEnabled(Workshop.Item item, bool checkContentFiles = true)
|
||||
{
|
||||
if (!item.Installed) return false;
|
||||
|
||||
string metaDataPath = Path.Combine(item.Directory.FullName, MetadataFileName);
|
||||
if (!File.Exists(metaDataPath))
|
||||
{
|
||||
throw new FileNotFoundException("Metadata file for the Workshop item \"" + item.Title + "\" not found. The file may be corrupted.");
|
||||
}
|
||||
|
||||
ContentPackage contentPackage = new ContentPackage(metaDataPath);
|
||||
//make sure the contentpackage file is present
|
||||
//(unless the package only contains submarine files, in which case we don't need a content package)
|
||||
if (contentPackage.Files.Any(f => f.Type != ContentType.Submarine) &&
|
||||
!File.Exists(GetWorkshopItemContentPackagePath(contentPackage)) &&
|
||||
!ContentPackage.List.Any(cp => cp.Name == contentPackage.Name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (checkContentFiles)
|
||||
{
|
||||
foreach (ContentFile contentFile in contentPackage.Files)
|
||||
{
|
||||
if (!File.Exists(contentFile.Path)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CheckWorkshopItemUpToDate(Workshop.Item item)
|
||||
{
|
||||
if (!item.Installed) return false;
|
||||
|
||||
string metaDataPath = Path.Combine(item.Directory.FullName, MetadataFileName);
|
||||
if (!File.Exists(metaDataPath))
|
||||
{
|
||||
throw new FileNotFoundException("Metadata file for the Workshop item \"" + item.Title + "\" not found. The file may be corrupted.");
|
||||
}
|
||||
|
||||
ContentPackage steamPackage = new ContentPackage(metaDataPath);
|
||||
ContentPackage myPackage = ContentPackage.List.Find(cp => cp.Name == steamPackage.Name);
|
||||
|
||||
if (myPackage?.InstallTime == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return item.Modified <= myPackage.InstallTime.Value;
|
||||
}
|
||||
|
||||
public static bool AutoUpdateWorkshopItems()
|
||||
{
|
||||
if (instance == null || !instance.isInitialized) { return false; }
|
||||
|
||||
bool itemsUpdated = false;
|
||||
foreach (ulong subscribedItemId in instance.client.Workshop.GetSubscribedItemIds())
|
||||
{
|
||||
var item = instance.client.Workshop.GetItem(subscribedItemId);
|
||||
if (item.Installed && CheckWorkshopItemEnabled(item) && !CheckWorkshopItemUpToDate(item))
|
||||
{
|
||||
if (!UpdateWorkshopItem(item, out string errorMsg))
|
||||
{
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemsUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return itemsUpdated;
|
||||
}
|
||||
|
||||
public static bool UpdateWorkshopItem(Workshop.Item item, out string errorMsg)
|
||||
{
|
||||
errorMsg = "";
|
||||
if (!item.Installed) { return false; }
|
||||
if (!DisableWorkShopItem(item, out errorMsg)) { return false; }
|
||||
if (!EnableWorkShopItem(item, allowFileOverwrite: false, errorMsg: out errorMsg)) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetWorkshopItemContentPackagePath(ContentPackage contentPackage)
|
||||
{
|
||||
string fileName = contentPackage.Name + ".xml";
|
||||
string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
|
||||
foreach (char c in invalidChars) fileName = fileName.Replace(c.ToString(), "");
|
||||
return Path.Combine("Data", "ContentPackages", fileName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
using Lidgren.Network;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class VoipCapture : VoipQueue, IDisposable
|
||||
{
|
||||
public static VoipCapture Instance
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private IntPtr captureDevice;
|
||||
|
||||
private Thread captureThread;
|
||||
|
||||
private bool capturing;
|
||||
|
||||
public double LastdB
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public DateTime LastEnqueueAudio;
|
||||
|
||||
public override byte QueueID
|
||||
{
|
||||
get
|
||||
{
|
||||
return GameMain.Client?.ID ?? 0;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public static void Create(string deviceName, UInt16? storedBufferID=null)
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
throw new Exception("Tried to instance more than one VoipCapture object");
|
||||
}
|
||||
|
||||
Instance = new VoipCapture(deviceName)
|
||||
{
|
||||
LatestBufferID = storedBufferID ?? BUFFER_COUNT - 1
|
||||
};
|
||||
}
|
||||
|
||||
private VoipCapture(string deviceName) : base(GameMain.Client?.ID ?? 0, true, false)
|
||||
{
|
||||
VoipConfig.SetupEncoding();
|
||||
|
||||
//set up capture device
|
||||
captureDevice = Alc.CaptureOpenDevice(deviceName, VoipConfig.FREQUENCY, ALFormat.Mono16, VoipConfig.BUFFER_SIZE * 5);
|
||||
|
||||
ALError alError = AL.GetError();
|
||||
AlcError alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != AlcError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to open capture device: " + alcError.ToString() + " (ALC)");
|
||||
}
|
||||
if (alError != ALError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to open capture device: " + alError.ToString() + " (AL)");
|
||||
}
|
||||
|
||||
Alc.CaptureStart(captureDevice);
|
||||
alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != AlcError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to start capturing: " + alcError.ToString());
|
||||
}
|
||||
|
||||
capturing = true;
|
||||
captureThread = new Thread(UpdateCapture)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "VoipCapture"
|
||||
};
|
||||
captureThread.Start();
|
||||
}
|
||||
|
||||
public static void ChangeCaptureDevice(string deviceName)
|
||||
{
|
||||
GameMain.Config.VoiceCaptureDevice = deviceName;
|
||||
|
||||
if (Instance != null)
|
||||
{
|
||||
UInt16 storedBufferID = Instance.LatestBufferID;
|
||||
Instance.Dispose();
|
||||
Create(GameMain.Config.VoiceCaptureDevice, storedBufferID);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateCapture()
|
||||
{
|
||||
short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
while (capturing)
|
||||
{
|
||||
AlcError alcError;
|
||||
Alc.GetInteger(captureDevice, AlcGetInteger.CaptureSamples, 1, out int sampleCount);
|
||||
|
||||
alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != AlcError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to determine sample count: " + alcError.ToString());
|
||||
}
|
||||
|
||||
if (sampleCount < VoipConfig.BUFFER_SIZE)
|
||||
{
|
||||
int sleepMs = (VoipConfig.BUFFER_SIZE - sampleCount) * 800 / VoipConfig.FREQUENCY;
|
||||
if (sleepMs < 5) sleepMs = 5;
|
||||
Thread.Sleep(sleepMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
GCHandle handle = GCHandle.Alloc(uncompressedBuffer, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
Alc.CaptureSamples(captureDevice, handle.AddrOfPinnedObject(), VoipConfig.BUFFER_SIZE);
|
||||
}
|
||||
finally
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
|
||||
alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != AlcError.NoError)
|
||||
{
|
||||
throw new Exception("Failed to capture samples: " + alcError.ToString());
|
||||
}
|
||||
|
||||
double maxAmplitude = 0.0f;
|
||||
for (int i = 0; i < VoipConfig.BUFFER_SIZE; i++)
|
||||
{
|
||||
double sampleVal = (double)uncompressedBuffer[i] / (double)short.MaxValue;
|
||||
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal));
|
||||
}
|
||||
double dB = Math.Min(20 * Math.Log10(maxAmplitude), 0.0);
|
||||
|
||||
LastdB = dB;
|
||||
|
||||
bool allowEnqueue = false;
|
||||
if (GameMain.WindowActive)
|
||||
{
|
||||
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Activity)
|
||||
{
|
||||
if (dB > GameMain.Config.NoiseGateThreshold)
|
||||
{
|
||||
allowEnqueue = true;
|
||||
}
|
||||
}
|
||||
else if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.PushToTalk)
|
||||
{
|
||||
if (PlayerInput.KeyDown(InputType.Voice))
|
||||
{
|
||||
allowEnqueue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowEnqueue)
|
||||
{
|
||||
LastEnqueueAudio = DateTime.Now;
|
||||
//encode audio and enqueue it
|
||||
lock (buffers)
|
||||
{
|
||||
int compressedCount = VoipConfig.Encoder.Encode(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
|
||||
EnqueueBuffer(compressedCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//enqueue silence
|
||||
lock (buffers)
|
||||
{
|
||||
EnqueueBuffer(0);
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(VoipConfig.BUFFER_SIZE * 800 / VoipConfig.FREQUENCY);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(NetBuffer msg)
|
||||
{
|
||||
lock (buffers)
|
||||
{
|
||||
base.Write(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Instance = null;
|
||||
capturing = false;
|
||||
captureThread.Join();
|
||||
captureThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
using Barotrauma.Sounds;
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class VoipClient : IDisposable
|
||||
{
|
||||
private GameClient gameClient;
|
||||
private NetClient netClient;
|
||||
private DateTime lastSendTime;
|
||||
private List<VoipQueue> queues;
|
||||
|
||||
private UInt16 storedBufferID = 0;
|
||||
|
||||
public VoipClient(GameClient gClient,NetClient nClient)
|
||||
{
|
||||
gameClient = gClient;
|
||||
netClient = nClient;
|
||||
|
||||
queues = new List<VoipQueue>();
|
||||
|
||||
lastSendTime = DateTime.Now;
|
||||
}
|
||||
|
||||
public void RegisterQueue(VoipQueue queue)
|
||||
{
|
||||
if (queue == VoipCapture.Instance) return;
|
||||
if (!queues.Contains(queue)) queues.Add(queue);
|
||||
}
|
||||
|
||||
public void UnregisterQueue(VoipQueue queue)
|
||||
{
|
||||
if (queues.Contains(queue)) queues.Remove(queue);
|
||||
}
|
||||
|
||||
public void SendToServer()
|
||||
{
|
||||
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Disabled)
|
||||
{
|
||||
if (VoipCapture.Instance != null)
|
||||
{
|
||||
storedBufferID = VoipCapture.Instance.LatestBufferID;
|
||||
VoipCapture.Instance.Dispose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (VoipCapture.Instance == null) VoipCapture.Create(GameMain.Config.VoiceCaptureDevice, storedBufferID);
|
||||
if (VoipCapture.Instance.EnqueuedTotalLength <= 0) return;
|
||||
}
|
||||
|
||||
if (DateTime.Now >= lastSendTime + VoipConfig.SEND_INTERVAL)
|
||||
{
|
||||
NetOutgoingMessage msg = netClient.CreateMessage();
|
||||
|
||||
msg.Write((byte)ClientPacketHeader.VOICE);
|
||||
msg.Write((byte)VoipCapture.Instance.QueueID);
|
||||
VoipCapture.Instance.Write(msg);
|
||||
|
||||
netClient.SendMessage(msg, NetDeliveryMethod.Unreliable);
|
||||
|
||||
lastSendTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(NetBuffer msg)
|
||||
{
|
||||
byte queueId = msg.ReadByte();
|
||||
VoipQueue queue = queues.Find(q => q.QueueID == queueId);
|
||||
|
||||
if (queue == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Couldn't find VoipQueue with id " + queueId.ToString() + "!", Color.Red);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (queue.Read(msg))
|
||||
{
|
||||
Client client = gameClient.ConnectedClients.Find(c => c.VoipQueue == queue);
|
||||
if (client.Muted || client.MutedLocally) { return; }
|
||||
|
||||
if (client.VoipSound == null)
|
||||
{
|
||||
DebugConsole.Log("Recreating voipsound " + queueId);
|
||||
client.VoipSound = new VoipSound(GameMain.SoundManager, client.VoipQueue);
|
||||
}
|
||||
|
||||
if (client.Character != null && !client.Character.IsDead && !client.Character.IsDead && client.Character.SpeechImpediment <= 100.0f)
|
||||
{
|
||||
var messageType = ChatMessage.CanUseRadio(client.Character, out WifiComponent radio) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
|
||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio;
|
||||
if (client.VoipSound.UseRadioFilter)
|
||||
{
|
||||
client.VoipSound.SetRange(radio.Range * 0.8f, radio.Range);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange);
|
||||
}
|
||||
if (!client.VoipSound.UseRadioFilter && Character.Controlled != null)
|
||||
{
|
||||
client.VoipSound.UseMuffleFilter = SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull);
|
||||
}
|
||||
}
|
||||
GameMain.NetLobbyScreen.SetPlayerSpeaking(client);
|
||||
GameMain.GameSession?.CrewManager?.SetPlayerSpeaking(client);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
VoipCapture.Instance?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Concentus.Enums;
|
||||
using Concentus.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
static partial class VoipConfig
|
||||
{
|
||||
public static bool Ready = false;
|
||||
|
||||
public const int FREQUENCY = 48000; //not amazing, but not bad audio quality
|
||||
public const int BUFFER_SIZE = 2880; //60ms window, the max Opus seems to support
|
||||
|
||||
public static OpusEncoder Encoder
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public static OpusDecoder Decoder
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public static void SetupEncoding()
|
||||
{
|
||||
if (!Ready)
|
||||
{
|
||||
Encoder = new OpusEncoder(FREQUENCY, 1, OpusApplication.OPUS_APPLICATION_VOIP);
|
||||
Encoder.Bandwidth = OpusBandwidth.OPUS_BANDWIDTH_AUTO;
|
||||
Encoder.Bitrate = 8 * MAX_COMPRESSED_SIZE * FREQUENCY / BUFFER_SIZE;
|
||||
Encoder.SignalType = OpusSignal.OPUS_SIGNAL_VOICE;
|
||||
|
||||
Decoder = new OpusDecoder(FREQUENCY, 1);
|
||||
|
||||
Ready = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,20 +15,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (value == allowSubVoting) return;
|
||||
allowSubVoting = value;
|
||||
GameMain.NetLobbyScreen.SubList.Enabled = value || GameMain.Server != null ||
|
||||
GameMain.NetLobbyScreen.SubList.Enabled = value ||
|
||||
(GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.SelectSub));
|
||||
GameMain.NetLobbyScreen.InfoFrame.FindChild("subvotes", true).Visible = value;
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
UpdateVoteTexts(value ? GameMain.Server.ConnectedClients : null, VoteType.Sub);
|
||||
GameMain.Server.UpdateVoteStatus();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateVoteTexts(null, VoteType.Sub);
|
||||
GameMain.NetLobbyScreen.SubList.Deselect();
|
||||
}
|
||||
UpdateVoteTexts(null, VoteType.Sub);
|
||||
GameMain.NetLobbyScreen.SubList.Deselect();
|
||||
}
|
||||
}
|
||||
public bool AllowModeVoting
|
||||
@@ -39,7 +31,7 @@ namespace Barotrauma
|
||||
if (value == allowModeVoting) return;
|
||||
allowModeVoting = value;
|
||||
GameMain.NetLobbyScreen.ModeList.Enabled =
|
||||
value || GameMain.Server != null ||
|
||||
value ||
|
||||
(GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.SelectMode));
|
||||
|
||||
GameMain.NetLobbyScreen.InfoFrame.FindChild("modevotes", true).Visible = value;
|
||||
@@ -51,17 +43,9 @@ namespace Barotrauma
|
||||
new Color(comp.TextColor.R, comp.TextColor.G, comp.TextColor.B,
|
||||
!allowModeVoting || ((GameModePreset)comp.UserData).Votable ? (byte)255 : (byte)100);
|
||||
}
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
UpdateVoteTexts(value ? GameMain.Server.ConnectedClients : null, VoteType.Mode);
|
||||
GameMain.Server.UpdateVoteStatus();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateVoteTexts(null, VoteType.Mode);
|
||||
GameMain.NetLobbyScreen.ModeList.Deselect();
|
||||
}
|
||||
|
||||
UpdateVoteTexts(null, VoteType.Mode);
|
||||
GameMain.NetLobbyScreen.ModeList.Deselect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,28 +75,11 @@ namespace Barotrauma
|
||||
if (clients == null) return;
|
||||
foreach (Client client in clients)
|
||||
{
|
||||
var clientNameBox = GameMain.NetLobbyScreen.PlayerList.Content.FindChild(client.Name);
|
||||
if (clientNameBox == null) continue;
|
||||
|
||||
var clientReady = clientNameBox.FindChild("clientready");
|
||||
if (!client.GetVote<bool>(VoteType.StartRound))
|
||||
var clientReady = GameMain.NetLobbyScreen.PlayerList.Content.FindChild(client)?.FindChild("clientready");
|
||||
if (clientReady != null)
|
||||
{
|
||||
if (clientReady != null) clientReady.Parent.RemoveChild(clientReady);
|
||||
clientReady.Visible = client.GetVote<bool>(VoteType.StartRound);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (clientReady == null)
|
||||
{
|
||||
new GUITickBox(new RectTransform(new Vector2(0.05f, 0.6f), clientNameBox.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.05f, 0.0f) }, "")
|
||||
{
|
||||
Selected = true,
|
||||
Enabled = false,
|
||||
ToolTip = "Ready to start",
|
||||
UserData = "clientready"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -140,8 +107,6 @@ namespace Barotrauma
|
||||
|
||||
public void ClientWrite(NetBuffer msg, VoteType voteType, object data)
|
||||
{
|
||||
if (GameMain.Server != null) return;
|
||||
|
||||
msg.Write((byte)voteType);
|
||||
|
||||
switch (voteType)
|
||||
@@ -176,10 +141,8 @@ namespace Barotrauma
|
||||
msg.WritePadBits();
|
||||
}
|
||||
|
||||
public void ClientRead(NetIncomingMessage inc)
|
||||
public void ClientRead(NetBuffer inc)
|
||||
{
|
||||
if (GameMain.Server != null) return;
|
||||
|
||||
AllowSubVoting = inc.ReadBoolean();
|
||||
if (allowSubVoting)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class WhiteListedPlayer
|
||||
{
|
||||
public WhiteListedPlayer(string name, UInt16 identifier, string ip)
|
||||
{
|
||||
Name = name;
|
||||
IP = ip;
|
||||
|
||||
UniqueIdentifier = identifier;
|
||||
}
|
||||
}
|
||||
|
||||
partial class WhiteList
|
||||
{
|
||||
private GUIComponent whitelistFrame;
|
||||
@@ -10,7 +24,17 @@ namespace Barotrauma.Networking
|
||||
private GUITextBox nameBox;
|
||||
private GUITextBox ipBox;
|
||||
private GUIButton addNewButton;
|
||||
|
||||
|
||||
public class LocalAdded
|
||||
{
|
||||
public string Name;
|
||||
public string IP;
|
||||
};
|
||||
|
||||
public bool localEnabled;
|
||||
public List<UInt16> localRemoved = new List<UInt16>();
|
||||
public List<LocalAdded> localAdded = new List<LocalAdded>();
|
||||
|
||||
public GUIComponent CreateWhiteListFrame(GUIComponent parent)
|
||||
{
|
||||
if (whitelistFrame != null)
|
||||
@@ -27,38 +51,26 @@ namespace Barotrauma.Networking
|
||||
|
||||
var enabledTick = new GUITickBox(new RectTransform(new Vector2(0.1f, 0.1f), whitelistFrame.RectTransform), TextManager.Get("WhiteListEnabled"))
|
||||
{
|
||||
Selected = Enabled,
|
||||
Selected = localEnabled,
|
||||
UpdateOrder = 1,
|
||||
OnSelected = (GUITickBox box) =>
|
||||
{
|
||||
Enabled = !Enabled;
|
||||
nameBox.Enabled = box.Selected;
|
||||
ipBox.Enabled = box.Selected;
|
||||
addNewButton.Enabled = box.Selected;
|
||||
|
||||
nameBox.Text = "";
|
||||
nameBox.Enabled = Enabled;
|
||||
ipBox.Text = "";
|
||||
ipBox.Enabled = Enabled;
|
||||
addNewButton.Enabled = false;
|
||||
localEnabled = box.Selected;
|
||||
|
||||
if (Enabled)
|
||||
{
|
||||
foreach (Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (!IsWhiteListed(c.Name, c.Connection.RemoteEndPoint.Address.ToString()))
|
||||
{
|
||||
whitelistedPlayers.Add(new WhiteListedPlayer(c.Name, c.Connection.RemoteEndPoint.Address.ToString()));
|
||||
if (whitelistFrame != null) CreateWhiteListFrame(whitelistFrame.Parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Save();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
localEnabled = Enabled;
|
||||
|
||||
var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), whitelistFrame.RectTransform));
|
||||
foreach (WhiteListedPlayer wlp in whitelistedPlayers)
|
||||
{
|
||||
if (localRemoved.Contains(wlp.UniqueIdentifier)) continue;
|
||||
string blockText = wlp.Name;
|
||||
if (!string.IsNullOrWhiteSpace(wlp.IP)) blockText += " (" + wlp.IP + ")";
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), listBox.Content.RectTransform),
|
||||
@@ -74,6 +86,23 @@ namespace Barotrauma.Networking
|
||||
};
|
||||
}
|
||||
|
||||
foreach (LocalAdded lad in localAdded)
|
||||
{
|
||||
string blockText = lad.Name;
|
||||
if (!string.IsNullOrWhiteSpace(lad.IP)) blockText += " (" + lad.IP + ")";
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), listBox.Content.RectTransform),
|
||||
blockText)
|
||||
{
|
||||
UserData = lad
|
||||
};
|
||||
|
||||
var removeButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), textBlock.RectTransform, Anchor.CenterRight), TextManager.Get("WhiteListRemove"))
|
||||
{
|
||||
UserData = lad,
|
||||
OnClicked = RemoveFromWhiteList
|
||||
};
|
||||
}
|
||||
|
||||
var nameArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), whitelistFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
@@ -105,8 +134,8 @@ namespace Barotrauma.Networking
|
||||
OnClicked = AddToWhiteList
|
||||
};
|
||||
|
||||
nameBox.Enabled = Enabled;
|
||||
ipBox.Enabled = Enabled;
|
||||
nameBox.Enabled = localEnabled;
|
||||
ipBox.Enabled = localEnabled;
|
||||
addNewButton.Enabled = false;
|
||||
|
||||
return parent;
|
||||
@@ -114,10 +143,20 @@ namespace Barotrauma.Networking
|
||||
|
||||
private bool RemoveFromWhiteList(GUIButton button, object obj)
|
||||
{
|
||||
WhiteListedPlayer wlp = obj as WhiteListedPlayer;
|
||||
if (wlp == null) return false;
|
||||
if (obj is WhiteListedPlayer)
|
||||
{
|
||||
WhiteListedPlayer wlp = obj as WhiteListedPlayer;
|
||||
if (wlp == null) return false;
|
||||
|
||||
RemoveFromWhiteList(wlp);
|
||||
if (!localRemoved.Contains(wlp.UniqueIdentifier)) localRemoved.Add(wlp.UniqueIdentifier);
|
||||
}
|
||||
else if (obj is LocalAdded)
|
||||
{
|
||||
LocalAdded lad = obj as LocalAdded;
|
||||
if (lad == null) return false;
|
||||
|
||||
if (localAdded.Contains(lad)) localAdded.Remove(lad);
|
||||
}
|
||||
|
||||
if (whitelistFrame != null)
|
||||
{
|
||||
@@ -131,8 +170,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nameBox.Text)) return false;
|
||||
if (whitelistedPlayers.Any(x => x.Name.ToLower() == nameBox.Text.ToLower() && x.IP == ipBox.Text)) return false;
|
||||
|
||||
AddToWhiteList(nameBox.Text, ipBox.Text);
|
||||
|
||||
if (!localAdded.Any(p => p.IP == ipBox.Text)) localAdded.Add(new LocalAdded() { Name = nameBox.Text, IP = ipBox.Text });
|
||||
|
||||
if (whitelistFrame != null)
|
||||
{
|
||||
@@ -140,5 +179,67 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClientAdminRead(NetBuffer incMsg)
|
||||
{
|
||||
bool hasPermission = incMsg.ReadBoolean();
|
||||
if (!hasPermission)
|
||||
{
|
||||
incMsg.ReadPadBits();
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOwner = incMsg.ReadBoolean();
|
||||
localEnabled = incMsg.ReadBoolean();
|
||||
Enabled = localEnabled;
|
||||
incMsg.ReadPadBits();
|
||||
|
||||
whitelistedPlayers.Clear();
|
||||
Int32 bannedPlayerCount = incMsg.ReadVariableInt32();
|
||||
for (int i = 0; i < bannedPlayerCount; i++)
|
||||
{
|
||||
string name = incMsg.ReadString();
|
||||
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
|
||||
|
||||
string ip = "";
|
||||
if (isOwner)
|
||||
{
|
||||
ip = incMsg.ReadString();
|
||||
}
|
||||
else
|
||||
{
|
||||
ip = "IP concealed by host";
|
||||
}
|
||||
DebugConsole.NewMessage("nerd: " + name, Color.Lime);
|
||||
whitelistedPlayers.Add(new WhiteListedPlayer(name, uniqueIdentifier, ip));
|
||||
}
|
||||
|
||||
if (whitelistFrame != null)
|
||||
{
|
||||
CreateWhiteListFrame(whitelistFrame.Parent);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientAdminWrite(NetBuffer outMsg)
|
||||
{
|
||||
outMsg.Write(localEnabled);
|
||||
outMsg.WritePadBits();
|
||||
|
||||
outMsg.Write((UInt16)localRemoved.Count);
|
||||
foreach (UInt16 uniqueId in localRemoved)
|
||||
{
|
||||
outMsg.Write(uniqueId);
|
||||
}
|
||||
|
||||
outMsg.Write((UInt16)localAdded.Count);
|
||||
foreach (LocalAdded lad in localAdded)
|
||||
{
|
||||
outMsg.Write(lad.Name);
|
||||
outMsg.Write(lad.IP); //TODO: ENCRYPT
|
||||
}
|
||||
|
||||
localRemoved.Clear();
|
||||
localAdded.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user