2f107db...5202af9

This commit is contained in:
Joonas Rikkonen
2019-03-18 21:42:26 +02:00
parent 58c92888b7
commit 044fd3344b
395 changed files with 27417 additions and 19754 deletions
@@ -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();
}
}
}