(d9829ac) v0.9.4.0
This commit is contained in:
@@ -7,9 +7,7 @@ namespace Barotrauma
|
||||
{
|
||||
public static Character Controlled = null;
|
||||
|
||||
partial void InitProjSpecific(XDocument doc)
|
||||
{
|
||||
}
|
||||
partial void InitProjSpecific(XElement mainElement) { }
|
||||
|
||||
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
|
||||
@@ -205,10 +205,10 @@ namespace Barotrauma
|
||||
ResetAutoComplete();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
RewriteInputToCommandLine(input);
|
||||
}
|
||||
|
||||
|
||||
//TODO: be more clever about it
|
||||
Thread.Sleep(10); //sleep for 10ms to not pin the CPU super hard
|
||||
}
|
||||
@@ -249,7 +249,7 @@ namespace Barotrauma
|
||||
try
|
||||
{
|
||||
Console.WriteLine(""); Console.CursorTop -= inputLines;
|
||||
|
||||
|
||||
string ln = input.Length > 0 ? AutoComplete(input, 0) : "";
|
||||
ln += new string(' ', consoleWidth - (ln.Length % consoleWidth));
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
@@ -738,10 +738,22 @@ namespace Barotrauma
|
||||
});
|
||||
AssignOnExecute("togglekarmatestmode|karmatestmode", (string[] args) =>
|
||||
{
|
||||
if (GameMain.Server?.KarmaManager == null) return;
|
||||
if (GameMain.Server?.KarmaManager == null) { return; }
|
||||
GameMain.Server.KarmaManager.TestMode = !GameMain.Server.KarmaManager.TestMode;
|
||||
NewMessage(GameMain.Server.KarmaManager.TestMode ? "Karma test mode enabled." : "Karma test mode disabled.", Color.LightGreen);
|
||||
});
|
||||
AssignOnClientRequestExecute("togglekarmatestmode|karmatestmode", (Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
{
|
||||
if (GameMain.Server?.KarmaManager == null) { return; }
|
||||
GameMain.Server.KarmaManager.TestMode = !GameMain.Server.KarmaManager.TestMode;
|
||||
NewMessage(GameMain.Server.KarmaManager.TestMode ?
|
||||
$"Karma test mode enabled by {client.Name}." :
|
||||
$"Karma test mode disabled by {client.Name}.",
|
||||
Color.LightGreen);
|
||||
GameMain.Server.SendDirectChatMessage(
|
||||
GameMain.Server.KarmaManager.TestMode ? "Karma test mode enabled." : "Karma test mode disabled.",
|
||||
client);
|
||||
});
|
||||
|
||||
AssignOnExecute("banendpoint", (string[] args) =>
|
||||
{
|
||||
@@ -829,13 +841,13 @@ namespace Barotrauma
|
||||
AssignOnExecute("setclientcharacter", (string[] args) =>
|
||||
{
|
||||
if (GameMain.Server == null) return;
|
||||
|
||||
|
||||
if (args.Length < 2)
|
||||
{
|
||||
ThrowError("Invalid parameters. The command should be formatted as \"setclientcharacter [client] [character]\". If the names consist of multiple words, you should surround them with quotation marks.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var client = GameMain.Server.ConnectedClients.Find(c => c.Name == args[0]);
|
||||
if (client == null)
|
||||
{
|
||||
@@ -844,6 +856,7 @@ namespace Barotrauma
|
||||
|
||||
var character = FindMatchingCharacter(args.Skip(1).ToArray(), false);
|
||||
GameMain.Server.SetClientCharacter(client, character);
|
||||
client.SpectateOnly = false;
|
||||
});
|
||||
|
||||
AssignOnExecute("difficulty|leveldifficulty", (string[] args) =>
|
||||
@@ -939,7 +952,7 @@ namespace Barotrauma
|
||||
TraitorManager traitorManager = GameMain.Server.TraitorManager;
|
||||
if (traitorManager == null || traitorManager.Traitors == null || !traitorManager.Traitors.Any())
|
||||
{
|
||||
GameMain.Server.SendTraitorMessage(client,"There are no traitors at the moment.", TraitorMessageType.Console);
|
||||
GameMain.Server.SendTraitorMessage(client, "There are no traitors at the moment.", "", TraitorMessageType.Console);
|
||||
return;
|
||||
}
|
||||
foreach (Traitor t in traitorManager.Traitors)
|
||||
@@ -953,11 +966,11 @@ namespace Barotrauma
|
||||
$"[traitorgoals]={traitorGoals.Substring(traitorGoalsStart)}",
|
||||
$"[traitorname]={t.Character.Name}",
|
||||
"Traitor [traitorname]'s current goals are:\n[traitorgoals]"
|
||||
}.Where(s => !string.IsNullOrEmpty(s))), TraitorMessageType.Console);
|
||||
}.Where(s => !string.IsNullOrEmpty(s))), t.Mission?.Identifier, TraitorMessageType.Console);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.Server.SendTraitorMessage(client, string.Format("- Traitor {0} has no current objective.", t.Character.Name), TraitorMessageType.Console);
|
||||
GameMain.Server.SendTraitorMessage(client, string.Format("- Traitor {0} has no current objective.", "", t.Character.Name), "", TraitorMessageType.Console);
|
||||
}
|
||||
}
|
||||
//GameMain.Server.SendTraitorMessage(client, "The code words are: " + traitorManager.CodeWords + ", response: " + traitorManager.CodeResponse + ".", TraitorMessageType.Console);
|
||||
@@ -1053,7 +1066,7 @@ namespace Barotrauma
|
||||
|
||||
commands.Add(new Command("servername", "servername [name]: Change the name of the server.", (string[] args) =>
|
||||
{
|
||||
GameMain.Server.Name = string.Join(" ", args);
|
||||
GameMain.Server.ServerName = string.Join(" ", args);
|
||||
GameMain.NetLobbyScreen.ChangeServerName(string.Join(" ", args));
|
||||
}));
|
||||
|
||||
@@ -1160,7 +1173,7 @@ namespace Barotrauma
|
||||
{
|
||||
return new string[][]
|
||||
{
|
||||
Submarine.Loaded.Select(s => s.Name).ToArray()
|
||||
Submarine.SavedSubmarines.Select(s => s.Name).ToArray()
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -1179,7 +1192,7 @@ namespace Barotrauma
|
||||
{
|
||||
return new string[][]
|
||||
{
|
||||
Submarine.Loaded.Select(s => s.Name).ToArray()
|
||||
Submarine.SavedSubmarines.Select(s => s.Name).ToArray()
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -1195,7 +1208,7 @@ namespace Barotrauma
|
||||
|
||||
commands.Add(new Command("startgame|startround|start", "start/startgame/startround: Start a new round.", (string[] args) =>
|
||||
{
|
||||
if (Screen.Selected == GameMain.GameScreen) return;
|
||||
if (Screen.Selected == GameMain.GameScreen) { return; }
|
||||
if (!GameMain.Server.StartGame()) NewMessage("Failed to start a new round", Color.Yellow);
|
||||
}));
|
||||
|
||||
@@ -1204,7 +1217,7 @@ namespace Barotrauma
|
||||
if (Screen.Selected == GameMain.NetLobbyScreen) return;
|
||||
GameMain.Server.EndGame();
|
||||
}));
|
||||
|
||||
|
||||
commands.Add(new Command("entitydata", "", (string[] args) =>
|
||||
{
|
||||
if (args.Length == 0) return;
|
||||
@@ -1528,7 +1541,7 @@ namespace Barotrauma
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
{
|
||||
Character killedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
|
||||
killedCharacter?.SetAllDamage(200.0f, 0.0f, 0.0f);
|
||||
killedCharacter?.SetAllDamage(200.0f, 0.0f, 0.0f);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1541,10 +1554,20 @@ namespace Barotrauma
|
||||
if (character != null)
|
||||
{
|
||||
GameMain.Server.SetClientCharacter(client, character);
|
||||
client.SpectateOnly = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
AssignOnClientRequestExecute(
|
||||
"freecam",
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
{
|
||||
GameMain.Server.SetClientCharacter(client, null);
|
||||
client.SpectateOnly = true;
|
||||
}
|
||||
);
|
||||
|
||||
AssignOnClientRequestExecute(
|
||||
"difficulty|leveldifficulty",
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
@@ -1791,7 +1814,7 @@ namespace Barotrauma
|
||||
ThrowError("Invalid parameters. The command should be formatted as \"setclientcharacter [client] [character]\". If the names consist of multiple words, you should surround them with quotation marks.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var client = GameMain.Server.ConnectedClients.Find(c => c.Name == args[0]);
|
||||
if (client == null)
|
||||
{
|
||||
@@ -1800,6 +1823,7 @@ namespace Barotrauma
|
||||
|
||||
var character = FindMatchingCharacter(args.Skip(1).ToArray(), false);
|
||||
GameMain.Server.SetClientCharacter(client, character);
|
||||
client.SpectateOnly = false;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1867,7 +1891,7 @@ namespace Barotrauma
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
GameMain.Server.CreateEntityEvent(wall);
|
||||
}
|
||||
}
|
||||
}));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Barotrauma
|
||||
|
||||
private static Stopwatch stopwatch;
|
||||
|
||||
public static HashSet<ContentPackage> SelectedPackages
|
||||
public static IEnumerable<ContentPackage> SelectedPackages
|
||||
{
|
||||
get { return Config?.SelectedContentPackages; }
|
||||
}
|
||||
|
||||
@@ -50,12 +50,18 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ShowQuestionPrompt("Enter a save name for the campaign:", (string saveName) =>
|
||||
{
|
||||
StartNewCampaign(saveName, GameMain.NetLobbyScreen.SelectedSub.FilePath, GameMain.NetLobbyScreen.LevelSeed);
|
||||
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName);
|
||||
StartNewCampaign(savePath, GameMain.NetLobbyScreen.SelectedSub.FilePath, GameMain.NetLobbyScreen.LevelSeed);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer).ToArray();
|
||||
if (saveFiles.Length == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("No save files found.");
|
||||
return;
|
||||
}
|
||||
DebugConsole.NewMessage("Saved campaigns:", Color.White);
|
||||
for (int i = 0; i < saveFiles.Length; i++)
|
||||
{
|
||||
@@ -64,9 +70,16 @@ namespace Barotrauma
|
||||
DebugConsole.ShowQuestionPrompt("Select a save file to load (0 - " + (saveFiles.Length - 1) + "):", (string selectedSave) =>
|
||||
{
|
||||
int saveIndex = -1;
|
||||
if (!int.TryParse(selectedSave, out saveIndex)) return;
|
||||
if (!int.TryParse(selectedSave, out saveIndex)) { return; }
|
||||
|
||||
LoadCampaign(saveFiles[saveIndex]);
|
||||
if (saveIndex < 0 || saveIndex >= saveFiles.Length)
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid save file index.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadCampaign(saveFiles[saveIndex]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -170,6 +183,7 @@ namespace Barotrauma
|
||||
msg.Write(Money);
|
||||
msg.Write(PurchasedHullRepairs);
|
||||
msg.Write(PurchasedItemRepairs);
|
||||
msg.Write(PurchasedLostShuttles);
|
||||
|
||||
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
|
||||
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
|
||||
@@ -196,6 +210,7 @@ namespace Barotrauma
|
||||
byte selectedMissionIndex = msg.ReadByte();
|
||||
bool purchasedHullRepairs = msg.ReadBoolean();
|
||||
bool purchasedItemRepairs = msg.ReadBoolean();
|
||||
bool purchasedLostShuttles = msg.ReadBoolean();
|
||||
UInt16 purchasedItemCount = msg.ReadUInt16();
|
||||
|
||||
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
|
||||
@@ -238,6 +253,24 @@ namespace Barotrauma
|
||||
Money += ItemRepairCost;
|
||||
}
|
||||
}
|
||||
if (purchasedLostShuttles != this.PurchasedLostShuttles)
|
||||
{
|
||||
if (GameMain.GameSession?.Submarine != null &&
|
||||
GameMain.GameSession.Submarine.LeftBehindSubDockingPortOccupied)
|
||||
{
|
||||
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
|
||||
}
|
||||
else if (purchasedLostShuttles && Money >= ShuttleReplaceCost)
|
||||
{
|
||||
this.PurchasedLostShuttles = true;
|
||||
Money -= ShuttleReplaceCost;
|
||||
}
|
||||
else if (!purchasedItemRepairs)
|
||||
{
|
||||
this.PurchasedLostShuttles = false;
|
||||
Money += ShuttleReplaceCost;
|
||||
}
|
||||
}
|
||||
|
||||
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||
if (Map.SelectedConnection != null)
|
||||
|
||||
@@ -5,21 +5,21 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ItemLabel : ItemComponent, IDrawableComponent
|
||||
{
|
||||
[Serialize("", true), Editable(100)]
|
||||
[Serialize("", true, description: "The text to display on the label."), Editable(100)]
|
||||
public string Text
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize("0.0,0.0,0.0,1.0", true)]
|
||||
[Editable, Serialize("0,0,0,255", true, description: "The color of the text displayed on the label.")]
|
||||
public Color TextColor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(1.0f, true)]
|
||||
[Editable, Serialize(1.0f, true, description: "The scale of the text displayed on the label.")]
|
||||
public float TextScale
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -13,5 +14,101 @@ namespace Barotrauma.Items.Components
|
||||
get { return unsentChanges; }
|
||||
set { unsentChanges = value; }
|
||||
}
|
||||
|
||||
|
||||
public void ServerRead(ClientNetObject type, IReadMessage msg, Barotrauma.Networking.Client c)
|
||||
{
|
||||
bool autoPilot = msg.ReadBoolean();
|
||||
bool dockingButtonClicked = msg.ReadBoolean();
|
||||
Vector2 newSteeringInput = targetVelocity;
|
||||
bool maintainPos = false;
|
||||
Vector2? newPosToMaintain = null;
|
||||
bool headingToStart = false;
|
||||
|
||||
if (autoPilot)
|
||||
{
|
||||
maintainPos = msg.ReadBoolean();
|
||||
if (maintainPos)
|
||||
{
|
||||
newPosToMaintain = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
}
|
||||
else
|
||||
{
|
||||
headingToStart = msg.ReadBoolean();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newSteeringInput = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
}
|
||||
|
||||
if (!item.CanClientAccess(c)) return;
|
||||
|
||||
user = c.Character;
|
||||
AutoPilot = autoPilot;
|
||||
|
||||
if (dockingButtonClicked)
|
||||
{
|
||||
item.SendSignal(0, "1", "toggle_docking", sender: null);
|
||||
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.GetComponentIndex(this), true });
|
||||
}
|
||||
|
||||
if (!AutoPilot)
|
||||
{
|
||||
steeringInput = newSteeringInput;
|
||||
steeringAdjustSpeed = MathHelper.Lerp(0.2f, 1.0f, c.Character.GetSkillLevel("helm") / 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaintainPos = newPosToMaintain != null;
|
||||
posToMaintain = newPosToMaintain;
|
||||
|
||||
if (posToMaintain == null)
|
||||
{
|
||||
LevelStartSelected = headingToStart;
|
||||
LevelEndSelected = !headingToStart;
|
||||
UpdatePath();
|
||||
}
|
||||
else
|
||||
{
|
||||
LevelStartSelected = false;
|
||||
LevelEndSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
//notify all clients of the changed state
|
||||
unsentChanges = true;
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Barotrauma.Networking.Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(autoPilot);
|
||||
msg.Write(extraData.Length > 2 && extraData[2] is bool && (bool)extraData[2]);
|
||||
|
||||
if (!autoPilot)
|
||||
{
|
||||
//no need to write steering info if autopilot is controlling
|
||||
msg.Write(steeringInput.X);
|
||||
msg.Write(steeringInput.Y);
|
||||
msg.Write(targetVelocity.X);
|
||||
msg.Write(targetVelocity.Y);
|
||||
msg.Write(steeringAdjustSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(posToMaintain != null);
|
||||
if (posToMaintain != null)
|
||||
{
|
||||
msg.Write(((Vector2)posToMaintain).X);
|
||||
msg.Write(((Vector2)posToMaintain).Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(LevelStartSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace Barotrauma
|
||||
{
|
||||
return;
|
||||
}
|
||||
Combine(combineTarget);
|
||||
Combine(combineTarget, c.Character);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
JobPreferences = new List<JobPrefab>(JobPrefab.List.GetRange(0, Math.Min(JobPrefab.List.Count, 3)));
|
||||
var jobs = JobPrefab.List.Values.ToList();
|
||||
// TODO: modding support?
|
||||
JobPreferences = new List<JobPrefab>(jobs.GetRange(0, Math.Min(jobs.Count, 3)));
|
||||
|
||||
VoipQueue = new VoipQueue(ID, true, true);
|
||||
GameMain.Server.VoipServer.RegisterQueue(VoipQueue);
|
||||
|
||||
@@ -23,6 +23,19 @@ namespace Barotrauma.Networking
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
private string serverName;
|
||||
|
||||
public string ServerName
|
||||
{
|
||||
get { return serverName; }
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) { return; }
|
||||
serverName = value.Replace(":", "").Replace(";", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<Client> connectedClients = new List<Client>();
|
||||
|
||||
//for keeping track of disconnected clients in case the reconnect shortly after
|
||||
@@ -39,7 +52,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
private ServerPeer serverPeer;
|
||||
public ServerPeer ServerPeer { get { return serverPeer; } }
|
||||
|
||||
|
||||
private DateTime refreshMasterTimer;
|
||||
private TimeSpan refreshMasterInterval = new TimeSpan(0, 0, 60);
|
||||
private bool registeredToMaster;
|
||||
@@ -58,7 +71,7 @@ namespace Barotrauma.Networking
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
private bool initiatedStartGame;
|
||||
private CoroutineHandle startGameCoroutine;
|
||||
|
||||
@@ -90,7 +103,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
get { return entityEventManager; }
|
||||
}
|
||||
|
||||
|
||||
public TimeSpan UpdateInterval
|
||||
{
|
||||
get { return updateInterval; }
|
||||
@@ -113,12 +126,13 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
name = name.Substring(0, NetConfig.ServerNameMaxLength);
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
|
||||
|
||||
this.serverName = name;
|
||||
|
||||
LastClientListUpdateID = 0;
|
||||
|
||||
serverSettings = new ServerSettings(this, name, port, queryPort, maxPlayers, isPublic, attemptUPnP);
|
||||
KarmaManager.SelectPreset(serverSettings.KarmaPreset);
|
||||
if (!string.IsNullOrEmpty(password))
|
||||
{
|
||||
serverSettings.SetPassword(password);
|
||||
@@ -129,7 +143,7 @@ namespace Barotrauma.Networking
|
||||
ownerSteamId = steamId;
|
||||
|
||||
entityEventManager = new ServerEntityEventManager(this);
|
||||
|
||||
|
||||
CoroutineManager.StartCoroutine(StartServer(isPublic));
|
||||
}
|
||||
|
||||
@@ -141,12 +155,12 @@ namespace Barotrauma.Networking
|
||||
Log("Starting the server...", ServerLog.MessageType.ServerMessage);
|
||||
if (!ownerSteamId.HasValue || ownerSteamId.Value == 0)
|
||||
{
|
||||
Log("Using Lidgren networking", ServerLog.MessageType.ServerMessage);
|
||||
Log("Using Lidgren networking. Manual port forwarding may be required. If players cannot connect to the server, you may want to use the in-game hosting menu (which uses SteamP2P networking and does not require port forwarding).", ServerLog.MessageType.ServerMessage);
|
||||
serverPeer = new LidgrenServerPeer(ownerKey, serverSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Using SteamP2P", ServerLog.MessageType.ServerMessage);
|
||||
Log("Using SteamP2P networking.", ServerLog.MessageType.ServerMessage);
|
||||
serverPeer = new SteamP2PServerPeer(ownerSteamId.Value, serverSettings);
|
||||
}
|
||||
|
||||
@@ -169,14 +183,14 @@ namespace Barotrauma.Networking
|
||||
Log("Error while starting the server (" + e.Message + ")", ServerLog.MessageType.Error);
|
||||
|
||||
System.Net.Sockets.SocketException socketException = e as System.Net.Sockets.SocketException;
|
||||
|
||||
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (serverPeer != null) serverPeer.Close("Error while starting the server");
|
||||
|
||||
|
||||
Environment.Exit(-1);
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
@@ -257,7 +271,7 @@ namespace Barotrauma.Networking
|
||||
newClient.AddKickVote(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LastClientListUpdateID++;
|
||||
|
||||
if (newClient.Connection == OwnerConnection)
|
||||
@@ -317,7 +331,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
var request = new RestRequest("masterserver3.php", Method.GET);
|
||||
request.AddParameter("action", "addserver");
|
||||
request.AddParameter("servername", name);
|
||||
request.AddParameter("servername", serverName);
|
||||
request.AddParameter("serverport", Port);
|
||||
request.AddParameter("currplayers", connectedClients.Count);
|
||||
request.AddParameter("maxplayers", serverSettings.MaxPlayers);
|
||||
@@ -434,12 +448,12 @@ namespace Barotrauma.Networking
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
#if CLIENT
|
||||
if (ShowNetStats) netStats.Update(deltaTime);
|
||||
if (ShowNetStats) { netStats.Update(deltaTime); }
|
||||
#endif
|
||||
if (!started) return;
|
||||
if (!started) { return; }
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
|
||||
fileSender.Update(deltaTime);
|
||||
KarmaManager.UpdateClients(ConnectedClients, deltaTime);
|
||||
|
||||
@@ -492,16 +506,16 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (Level.Loaded?.EndOutpost != null)
|
||||
{
|
||||
bool charactersInsideOutpost = connectedClients.Any(c =>
|
||||
c.Character != null &&
|
||||
!c.Character.IsDead &&
|
||||
bool charactersInsideOutpost = connectedClients.Any(c =>
|
||||
c.Character != null &&
|
||||
!c.Character.IsDead &&
|
||||
c.Character.Submarine == Level.Loaded.EndOutpost);
|
||||
|
||||
//level finished if the sub is docked to the outpost
|
||||
//or very close and someone from the crew made it inside the outpost
|
||||
subAtLevelEnd =
|
||||
subAtLevelEnd =
|
||||
Submarine.MainSub.DockedTo.Contains(Level.Loaded.EndOutpost) ||
|
||||
(Submarine.MainSub.AtEndPosition && charactersInsideOutpost);
|
||||
(Submarine.MainSub.AtEndPosition && charactersInsideOutpost);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -510,7 +524,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
float endRoundDelay = 1.0f;
|
||||
if (serverSettings.AutoRestart && isCrewDead)
|
||||
if (TraitorManager?.ShouldEndRound ?? false)
|
||||
{
|
||||
endRoundDelay = 5.0f;
|
||||
endRoundTimer += deltaTime;
|
||||
}
|
||||
else if (serverSettings.AutoRestart && isCrewDead)
|
||||
{
|
||||
endRoundDelay = 5.0f;
|
||||
endRoundTimer += deltaTime;
|
||||
@@ -533,10 +552,14 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
endRoundTimer = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
if (endRoundTimer >= endRoundDelay)
|
||||
{
|
||||
if (serverSettings.AutoRestart && isCrewDead)
|
||||
if (TraitorManager?.ShouldEndRound ?? false)
|
||||
{
|
||||
Log("Ending round (a traitor completed their mission)", ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
else if (serverSettings.AutoRestart && isCrewDead)
|
||||
{
|
||||
Log("Ending round (entire crew dead)", ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
@@ -572,8 +595,8 @@ namespace Barotrauma.Networking
|
||||
if (serverSettings.AutoRestart)
|
||||
{
|
||||
//autorestart if there are any non-spectators on the server (ignoring the server owner)
|
||||
bool shouldAutoRestart = connectedClients.Any(c =>
|
||||
c.Connection != OwnerConnection &&
|
||||
bool shouldAutoRestart = connectedClients.Any(c =>
|
||||
c.Connection != OwnerConnection &&
|
||||
(!c.SpectateOnly || !serverSettings.AllowSpectating));
|
||||
|
||||
if (shouldAutoRestart != autoRestartTimerRunning)
|
||||
@@ -629,7 +652,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<Client> kickAFK = connectedClients.FindAll(c =>
|
||||
IEnumerable<Client> kickAFK = connectedClients.FindAll(c =>
|
||||
c.KickAFKTimer >= serverSettings.KickAFKTime &&
|
||||
(OwnerConnection == null || c.Connection != OwnerConnection));
|
||||
foreach (Client c in kickAFK)
|
||||
@@ -639,6 +662,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
serverPeer.Update(deltaTime);
|
||||
|
||||
//don't run the rest of the method if something in serverPeer.Update causes the server to shutdown
|
||||
if (!started) { return; }
|
||||
|
||||
// if update interval has passed
|
||||
if (updateTimer < DateTime.Now)
|
||||
{
|
||||
@@ -662,7 +688,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"GameServer.Update:ClientWriteFailed" + e.StackTrace,
|
||||
"GameServer.Update:ClientWriteFailed" + e.StackTrace,
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
errorMsg);
|
||||
}
|
||||
@@ -705,7 +731,7 @@ namespace Barotrauma.Networking
|
||||
serverSettings.ServerDetailsChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ReadDataMessage(NetworkConnection sender, IReadMessage inc)
|
||||
{
|
||||
var connectedClient = connectedClients.Find(c => c.Connection == sender);
|
||||
@@ -748,7 +774,7 @@ namespace Barotrauma.Networking
|
||||
if (matchingSub == null)
|
||||
{
|
||||
SendDirectChatMessage(
|
||||
TextManager.GetWithVariable("CampaignStartFailedSubNotFound", "[subname]", subName),
|
||||
TextManager.GetWithVariable("CampaignStartFailedSubNotFound", "[subname]", subName),
|
||||
connectedClient, ChatMessageType.MessageBox);
|
||||
}
|
||||
else
|
||||
@@ -890,8 +916,8 @@ namespace Barotrauma.Networking
|
||||
c.LastRecvLobbyUpdate = NetIdUtils.Clamp(inc.ReadUInt16(), c.LastRecvLobbyUpdate, GameMain.NetLobbyScreen.LastUpdateID);
|
||||
c.LastRecvChatMsgID = NetIdUtils.Clamp(inc.ReadUInt16(), c.LastRecvChatMsgID, c.LastChatMsgQueueID);
|
||||
c.LastRecvClientListUpdate = NetIdUtils.Clamp(inc.ReadUInt16(), c.LastRecvClientListUpdate, LastClientListUpdateID);
|
||||
|
||||
TryChangeClientName(c, inc.ReadString());
|
||||
|
||||
TryChangeClientName(c, inc);
|
||||
|
||||
c.LastRecvCampaignSave = inc.ReadUInt16();
|
||||
if (c.LastRecvCampaignSave > 0)
|
||||
@@ -907,7 +933,7 @@ namespace Barotrauma.Networking
|
||||
campaign.DiscardClientCharacterData(c);
|
||||
}
|
||||
|
||||
//the client has a campaign save for another campaign
|
||||
//the client has a campaign save for another campaign
|
||||
//(the server started a new campaign and the client isn't aware of it yet?)
|
||||
if (campaign.CampaignID != campaignID)
|
||||
{
|
||||
@@ -963,7 +989,7 @@ namespace Barotrauma.Networking
|
||||
UInt16 lastRecvChatMsgID = inc.ReadUInt16();
|
||||
UInt16 lastRecvEntityEventID = inc.ReadUInt16();
|
||||
UInt16 lastRecvClientListUpdate = inc.ReadUInt16();
|
||||
|
||||
|
||||
//last msgs we've created/sent, the client IDs should never be higher than these
|
||||
UInt16 lastEntityEventID = entityEventManager.Events.Count == 0 ? (UInt16)0 : entityEventManager.Events.Last().ID;
|
||||
|
||||
@@ -1071,10 +1097,10 @@ namespace Barotrauma.Networking
|
||||
return;
|
||||
}
|
||||
|
||||
//clients are allowed to end the round by talking with the watchman in multiplayer
|
||||
//clients are allowed to end the round by talking with the watchman in multiplayer
|
||||
//campaign even if they don't have the special permission
|
||||
bool peekBool = inc.ReadBoolean(); inc.BitPosition--;
|
||||
if (command == ClientPermissions.ManageRound && peekBool &&
|
||||
if (command == ClientPermissions.ManageRound && peekBool &&
|
||||
GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
if (!mpCampaign.AllowedToEndRound(sender.Character) && !sender.HasPermission(command))
|
||||
@@ -1192,7 +1218,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
msg.Write(saveFile);
|
||||
}
|
||||
|
||||
|
||||
serverPeer.Send(msg, sender.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
else
|
||||
@@ -1267,10 +1293,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
c.Character.ClientDisconnected = true;
|
||||
}
|
||||
|
||||
|
||||
ClientWriteLobby(c);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign &&
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign &&
|
||||
GameMain.NetLobbyScreen.SelectedMode == campaign.Preset &&
|
||||
NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave))
|
||||
{
|
||||
@@ -1284,7 +1310,7 @@ namespace Barotrauma.Networking
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!fileSender.ActiveTransfers.Any(t => t.Connection == c.Connection && t.FileType == FileTransferType.CampaignSave))
|
||||
{
|
||||
fileSender.StartTransfer(c.Connection, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
|
||||
@@ -1312,14 +1338,15 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
outmsg.Write(subList[i].Name);
|
||||
outmsg.Write(subList[i].MD5Hash.ToString());
|
||||
outmsg.Write(subList[i].RequiredContentPackagesInstalled);
|
||||
}
|
||||
|
||||
outmsg.Write(GameStarted);
|
||||
outmsg.Write(serverSettings.AllowSpectating);
|
||||
|
||||
|
||||
c.WritePermissions(outmsg);
|
||||
}
|
||||
|
||||
|
||||
private void ClientWriteIngame(Client c)
|
||||
{
|
||||
//don't send position updates to characters who are still midround syncing
|
||||
@@ -1392,7 +1419,7 @@ namespace Barotrauma.Networking
|
||||
while (!c.NeedsMidRoundSync && c.PendingPositionUpdates.Count > 0)
|
||||
{
|
||||
var entity = c.PendingPositionUpdates.Peek();
|
||||
if (entity == null || entity.Removed ||
|
||||
if (entity == null || entity.Removed ||
|
||||
(entity is Item item && item.PositionUpdateInterval == float.PositiveInfinity))
|
||||
{
|
||||
c.PendingPositionUpdates.Dequeue();
|
||||
@@ -1436,7 +1463,7 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
|
||||
|
||||
serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -1476,24 +1503,25 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame2:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
|
||||
serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable);
|
||||
}
|
||||
|
||||
serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteClientList(Client c, IWriteMessage outmsg)
|
||||
{
|
||||
bool hasChanged = NetIdUtils.IdMoreRecent(LastClientListUpdateID, c.LastRecvClientListUpdate);
|
||||
if (!hasChanged) { return; }
|
||||
|
||||
|
||||
outmsg.Write((byte)ServerNetObject.CLIENT_LIST);
|
||||
outmsg.Write(LastClientListUpdateID);
|
||||
|
||||
|
||||
outmsg.Write((byte)connectedClients.Count);
|
||||
foreach (Client client in connectedClients)
|
||||
{
|
||||
outmsg.Write(client.ID);
|
||||
outmsg.Write(client.SteamID);
|
||||
outmsg.Write(client.NameID);
|
||||
outmsg.Write(client.Name);
|
||||
outmsg.Write(client.Character == null || !gameStarted ? (ushort)0 : client.Character.ID);
|
||||
outmsg.Write(client.Muted);
|
||||
@@ -1512,25 +1540,28 @@ namespace Barotrauma.Networking
|
||||
outmsg.Write((byte)ServerNetObject.SYNC_IDS);
|
||||
|
||||
int settingsBytes = outmsg.LengthBytes;
|
||||
int initialUpdateBytes = 0;
|
||||
|
||||
IWriteMessage settingsBuf = null;
|
||||
if (NetIdUtils.IdMoreRecent(GameMain.NetLobbyScreen.LastUpdateID, c.LastRecvLobbyUpdate))
|
||||
{
|
||||
outmsg.Write(true);
|
||||
outmsg.WritePadBits();
|
||||
|
||||
|
||||
outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
|
||||
IWriteMessage settingsBuf = new ReadWriteMessage();
|
||||
settingsBuf = new ReadWriteMessage();
|
||||
serverSettings.ServerWrite(settingsBuf, c);
|
||||
|
||||
outmsg.Write((UInt16)settingsBuf.LengthBytes);
|
||||
outmsg.Write(settingsBuf.Buffer,0,settingsBuf.LengthBytes);
|
||||
outmsg.Write(settingsBuf.Buffer, 0, settingsBuf.LengthBytes);
|
||||
|
||||
outmsg.Write(c.LastRecvLobbyUpdate < 1);
|
||||
if (c.LastRecvLobbyUpdate < 1)
|
||||
{
|
||||
isInitialUpdate = true;
|
||||
initialUpdateBytes = outmsg.LengthBytes;
|
||||
ClientWriteInitial(c, outmsg);
|
||||
initialUpdateBytes = outmsg.LengthBytes - initialUpdateBytes;
|
||||
}
|
||||
outmsg.Write(GameMain.NetLobbyScreen.SelectedSub.Name);
|
||||
outmsg.Write(GameMain.NetLobbyScreen.SelectedSub.MD5Hash.ToString());
|
||||
@@ -1572,7 +1603,7 @@ namespace Barotrauma.Networking
|
||||
int campaignBytes = outmsg.LengthBytes;
|
||||
var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
|
||||
if (outmsg.LengthBytes < MsgConstants.MTU - 500 &&
|
||||
campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode &&
|
||||
campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode &&
|
||||
NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.LastRecvCampaignUpdate))
|
||||
{
|
||||
outmsg.Write(true);
|
||||
@@ -1600,7 +1631,7 @@ namespace Barotrauma.Networking
|
||||
chatMessageBytes = outmsg.LengthBytes - outmsg.LengthBytes;
|
||||
|
||||
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
|
||||
|
||||
if (isInitialUpdate)
|
||||
{
|
||||
//the initial update may be very large if the host has a large number
|
||||
@@ -1619,13 +1650,23 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (outmsg.LengthBytes > MsgConstants.MTU)
|
||||
{
|
||||
string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU + ")";
|
||||
string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU + ")\n";
|
||||
errorMsg +=
|
||||
" Client list size: " + clientListBytes + " bytes\n" +
|
||||
" Chat message size: " + chatMessageBytes + " bytes\n" +
|
||||
" Campaign size: " + campaignBytes + " bytes\n" +
|
||||
" Settings size: " + settingsBytes + " bytes\n\n";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
" Settings size: " + settingsBytes + " bytes\n";
|
||||
if (initialUpdateBytes > 0)
|
||||
{
|
||||
errorMsg +=
|
||||
" Initial update size: " + settingsBuf.LengthBytes + " bytes\n";
|
||||
}
|
||||
if (settingsBuf != null)
|
||||
{
|
||||
errorMsg +=
|
||||
" Settings buffer size: " + settingsBuf.LengthBytes + " bytes\n";
|
||||
}
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:ClientWriteLobby" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
|
||||
@@ -1649,6 +1690,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
public bool StartGame()
|
||||
{
|
||||
if (initiatedStartGame || gameStarted) { return false; }
|
||||
|
||||
Log("Starting a new round...", ServerLog.MessageType.ServerMessage);
|
||||
|
||||
Submarine selectedSub = null;
|
||||
@@ -1691,7 +1734,7 @@ namespace Barotrauma.Networking
|
||||
private IEnumerable<object> InitiateStartGame(Submarine selectedSub, Submarine selectedShuttle, bool usingShuttle, GameModePreset selectedMode)
|
||||
{
|
||||
initiatedStartGame = true;
|
||||
|
||||
|
||||
if (connectedClients.Any())
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
@@ -1705,7 +1748,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write(selectedShuttle.MD5Hash.Hash);
|
||||
|
||||
connectedClients.ForEach(c => c.ReadyToStart = false);
|
||||
|
||||
|
||||
foreach (NetworkConnection conn in connectedClients.Select(c => c.Connection))
|
||||
{
|
||||
serverPeer.Send(msg, conn, DeliveryMethod.Reliable);
|
||||
@@ -1725,7 +1768,7 @@ namespace Barotrauma.Networking
|
||||
while (fileSender.ActiveTransfers.Count > 0 && waitForTransfersTimer > 0.0f)
|
||||
{
|
||||
waitForTransfersTimer -= CoroutineManager.UnscaledDeltaTime;
|
||||
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
}
|
||||
@@ -1739,7 +1782,7 @@ namespace Barotrauma.Networking
|
||||
private IEnumerable<object> StartGame(Submarine selectedSub, Submarine selectedShuttle, bool usingShuttle, GameModePreset selectedMode)
|
||||
{
|
||||
entityEventManager.Clear();
|
||||
|
||||
|
||||
roundStartSeed = DateTime.Now.Millisecond;
|
||||
Rand.SetSyncedSeed(roundStartSeed);
|
||||
|
||||
@@ -1767,8 +1810,16 @@ namespace Barotrauma.Networking
|
||||
GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, (MissionType)GameMain.NetLobbyScreen.MissionTypeIndex);
|
||||
}
|
||||
|
||||
List<Client> playingClients = new List<Client>(connectedClients);
|
||||
if (serverSettings.AllowSpectating)
|
||||
{
|
||||
playingClients.RemoveAll(c => c.SpectateOnly);
|
||||
}
|
||||
//always allow the server owner to spectate even if it's disallowed in server settings
|
||||
playingClients.RemoveAll(c => c.Connection == OwnerConnection && c.SpectateOnly);
|
||||
|
||||
if (GameMain.GameSession.GameMode.Mission != null &&
|
||||
GameMain.GameSession.GameMode.Mission.AssignTeamIDs(connectedClients))
|
||||
GameMain.GameSession.GameMode.Mission.AssignTeamIDs(playingClients))
|
||||
{
|
||||
teamCount = 2;
|
||||
}
|
||||
@@ -1797,10 +1848,18 @@ namespace Barotrauma.Networking
|
||||
Log("Level seed: " + GameMain.NetLobbyScreen.LevelSeed, ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
|
||||
if (GameMain.GameSession.Submarine.IsFileCorrupted)
|
||||
{
|
||||
CoroutineManager.StopCoroutines(startGameCoroutine);
|
||||
initiatedStartGame = false;
|
||||
SendChatMessage(TextManager.FormatServerMessage($"SubLoadError~[subname]={GameMain.GameSession.Submarine.Name}"), ChatMessageType.Error);
|
||||
yield return CoroutineStatus.Failure;
|
||||
}
|
||||
|
||||
MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode;
|
||||
bool missionAllowRespawn = campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn);
|
||||
|
||||
if (serverSettings.AllowRespawn && missionAllowRespawn) respawnManager = new RespawnManager(this, usingShuttle ? selectedShuttle : null);
|
||||
if (serverSettings.AllowRespawn && missionAllowRespawn) { respawnManager = new RespawnManager(this, usingShuttle ? selectedShuttle : null); }
|
||||
|
||||
entityEventManager.RefreshEntityIDs();
|
||||
|
||||
@@ -1816,15 +1875,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
//find the clients in this team
|
||||
List<Client> teamClients = teamCount == 1 ?
|
||||
new List<Client>(connectedClients) :
|
||||
connectedClients.FindAll(c => c.TeamID == teamID);
|
||||
if (serverSettings.AllowSpectating)
|
||||
{
|
||||
teamClients.RemoveAll(c => c.SpectateOnly);
|
||||
}
|
||||
//always allow the server owner to spectate even if it's disallowed in server settings
|
||||
teamClients.RemoveAll(c => c.Connection == OwnerConnection && c.SpectateOnly);
|
||||
List<Client> teamClients = teamCount == 1 ?
|
||||
new List<Client>(playingClients) :
|
||||
playingClients.FindAll(c => c.TeamID == teamID);
|
||||
|
||||
if (!teamClients.Any() && n > 0) { continue; }
|
||||
|
||||
@@ -1843,7 +1896,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (client.CharacterInfo == null)
|
||||
{
|
||||
client.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, client.Name);
|
||||
client.CharacterInfo = new CharacterInfo(Character.HumanSpeciesName, client.Name);
|
||||
}
|
||||
characterInfos.Add(client.CharacterInfo);
|
||||
if (client.CharacterInfo.Job == null || client.CharacterInfo.Job.Prefab != client.AssignedJob)
|
||||
@@ -1851,12 +1904,12 @@ namespace Barotrauma.Networking
|
||||
client.CharacterInfo.Job = new Job(client.AssignedJob);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<CharacterInfo> bots = new List<CharacterInfo>();
|
||||
int botsToSpawn = serverSettings.BotSpawnMode == BotSpawnMode.Fill ? serverSettings.BotCount - characterInfos.Count : serverSettings.BotCount;
|
||||
for (int i = 0; i < botsToSpawn; i++)
|
||||
{
|
||||
var botInfo = new CharacterInfo(Character.HumanConfigFile);
|
||||
var botInfo = new CharacterInfo(Character.HumanSpeciesName);
|
||||
characterInfos.Add(botInfo);
|
||||
bots.Add(botInfo);
|
||||
}
|
||||
@@ -1929,9 +1982,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
GameMain.GameScreen.Select();
|
||||
|
||||
|
||||
Log("Round started.", ServerLog.MessageType.ServerMessage);
|
||||
|
||||
|
||||
gameStarted = true;
|
||||
initiatedStartGame = false;
|
||||
GameMain.ResetFrameTime();
|
||||
@@ -1988,7 +2041,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write(serverSettings.AllowRagdollButton);
|
||||
|
||||
serverSettings.WriteMonsterEnabled(msg);
|
||||
|
||||
|
||||
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2022,10 +2075,10 @@ namespace Barotrauma.Networking
|
||||
"[endsummary]=" + roundSummary.Substring(roundSummaryStart),
|
||||
"[endsummary]\n\n[endsummary.traitorinfo]"
|
||||
}.Where(s => !string.IsNullOrEmpty(s)));
|
||||
|
||||
|
||||
Mission mission = GameMain.GameSession.Mission;
|
||||
GameMain.GameSession.GameMode.End(endMessage);
|
||||
|
||||
|
||||
endRoundTimer = 0.0f;
|
||||
|
||||
if (serverSettings.AutoRestart)
|
||||
@@ -2036,7 +2089,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
if (serverSettings.SaveServerLogs) serverSettings.ServerLog.Save();
|
||||
|
||||
|
||||
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
|
||||
entityEventManager.Clear();
|
||||
@@ -2063,7 +2116,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write(endMessage);
|
||||
msg.Write(mission != null && mission.Completed);
|
||||
msg.Write(GameMain.GameSession?.WinningTeam == null ? (byte)0 : (byte)GameMain.GameSession.WinningTeam);
|
||||
|
||||
|
||||
foreach (Client client in connectedClients)
|
||||
{
|
||||
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
||||
@@ -2080,7 +2133,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
GameMain.NetLobbyScreen.RandomizeSettings();
|
||||
}
|
||||
|
||||
|
||||
public override void AddChatMessage(ChatMessage message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message.Text)) { return; }
|
||||
@@ -2089,9 +2142,14 @@ namespace Barotrauma.Networking
|
||||
base.AddChatMessage(message);
|
||||
}
|
||||
|
||||
private bool TryChangeClientName(Client c, string newName)
|
||||
private bool TryChangeClientName(Client c, IReadMessage inc)
|
||||
{
|
||||
if (c == null || string.IsNullOrEmpty(newName)) { return false; }
|
||||
UInt16 nameId = inc.ReadUInt16();
|
||||
string newName = inc.ReadString();
|
||||
|
||||
if (c == null || string.IsNullOrEmpty(newName) || !NetIdUtils.IdMoreRecent(nameId, c.NameID)) { return false; }
|
||||
|
||||
c.NameID = nameId;
|
||||
|
||||
newName = Client.SanitizeName(newName);
|
||||
if (newName == c.Name) { return false; }
|
||||
@@ -2107,13 +2165,13 @@ namespace Barotrauma.Networking
|
||||
SendDirectChatMessage("Could not change your name to \"" + newName + "\" (the name contains disallowed symbols).", c, ChatMessageType.MessageBox);
|
||||
return false;
|
||||
}
|
||||
if (Homoglyphs.Compare(newName.ToLower(), Name.ToLower()))
|
||||
if (Homoglyphs.Compare(newName.ToLower(), ServerName.ToLower()))
|
||||
{
|
||||
SendDirectChatMessage("Could not change your name to \"" + newName + "\" (too similar to the server's name).", c, ChatMessageType.MessageBox);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Client nameTaken = ConnectedClients.Find(c2 => c != c2 && Homoglyphs.Compare(c2.Name.ToLower(), newName.ToLower()));
|
||||
if (nameTaken != null)
|
||||
{
|
||||
@@ -2207,7 +2265,7 @@ namespace Barotrauma.Networking
|
||||
lidgrenConn.IPEndPoint.Address.ToString();
|
||||
if (range) { ip = serverSettings.BanList.ToRange(ip); }
|
||||
}
|
||||
|
||||
|
||||
serverSettings.BanList.BanPlayer(client.Name, ip, reason, duration);
|
||||
}
|
||||
if (client.SteamID > 0)
|
||||
@@ -2280,7 +2338,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (client.HasKickVoteFrom(c)) { previousPlayer.KickVoters.Add(c); }
|
||||
}
|
||||
|
||||
|
||||
serverPeer.Disconnect(client.Connection, targetmsg);
|
||||
client.Dispose();
|
||||
connectedClients.Remove(client);
|
||||
@@ -2288,7 +2346,7 @@ namespace Barotrauma.Networking
|
||||
KarmaManager.OnClientDisconnected(client);
|
||||
|
||||
UpdateVoteStatus();
|
||||
|
||||
|
||||
SendChatMessage(msg, ChatMessageType.Server);
|
||||
|
||||
UpdateCrewFrame();
|
||||
@@ -2360,7 +2418,7 @@ namespace Barotrauma.Networking
|
||||
default:
|
||||
if (command != "")
|
||||
{
|
||||
if (command.ToLower() == name.ToLower())
|
||||
if (command.ToLower() == serverName.ToLower())
|
||||
{
|
||||
//a private message to the host
|
||||
if (OwnerConnection != null)
|
||||
@@ -2411,7 +2469,7 @@ namespace Barotrauma.Networking
|
||||
//msg sent by the server
|
||||
if (senderCharacter == null)
|
||||
{
|
||||
senderName = name;
|
||||
senderName = serverName;
|
||||
}
|
||||
else //msg sent by an AI character
|
||||
{
|
||||
@@ -2443,14 +2501,14 @@ namespace Barotrauma.Networking
|
||||
//msg sent by the server
|
||||
if (senderCharacter == null)
|
||||
{
|
||||
senderName = name;
|
||||
senderName = serverName;
|
||||
}
|
||||
else //sent by an AI character, not allowed when the game is not running
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else //msg sent by a client
|
||||
else //msg sent by a client
|
||||
{
|
||||
//game not started -> clients can only send normal and private chatmessages
|
||||
if (type != ChatMessageType.Private) type = ChatMessageType.Default;
|
||||
@@ -2482,7 +2540,7 @@ namespace Barotrauma.Networking
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == ChatMessageType.Server)
|
||||
if (type == ChatMessageType.Server || type == ChatMessageType.Error)
|
||||
{
|
||||
senderName = null;
|
||||
senderCharacter = null;
|
||||
@@ -2570,7 +2628,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
string myReceivedMessage = message.Text;
|
||||
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(myReceivedMessage))
|
||||
{
|
||||
AddChatMessage(new OrderChatMessage(message.Order, message.OrderOption, myReceivedMessage, message.TargetEntity, message.TargetCharacter, message.Sender));
|
||||
@@ -2603,7 +2661,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
Client.UpdateKickVotes(connectedClients);
|
||||
|
||||
var clientsToKick = connectedClients.FindAll(c =>
|
||||
var clientsToKick = connectedClients.FindAll(c =>
|
||||
c.Connection != OwnerConnection &&
|
||||
c.KickVoteCount >= connectedClients.Count * serverSettings.KickVoteRequiredRatio);
|
||||
foreach (Client c in clientsToKick)
|
||||
@@ -2641,7 +2699,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write((byte)ServerNetObject.VOTE);
|
||||
serverSettings.Voting.ServerWrite(msg);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
|
||||
|
||||
foreach (var c in recipients)
|
||||
{
|
||||
serverPeer.Send(msg, c.Connection, DeliveryMethod.Reliable);
|
||||
@@ -2702,7 +2760,7 @@ namespace Barotrauma.Networking
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
SendClientPermissions(recipient, client);
|
||||
yield return CoroutineStatus.Success;
|
||||
@@ -2718,7 +2776,7 @@ namespace Barotrauma.Networking
|
||||
client.WritePermissions(msg);
|
||||
serverPeer.Send(msg, recipient.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
|
||||
public void GiveAchievement(Character character, string achievementIdentifier)
|
||||
{
|
||||
achievementIdentifier = achievementIdentifier.ToLowerInvariant();
|
||||
@@ -2740,22 +2798,18 @@ namespace Barotrauma.Networking
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ServerPacketHeader.ACHIEVEMENT);
|
||||
msg.Write(achievementIdentifier);
|
||||
|
||||
|
||||
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public void SendTraitorMessage(Client client, string message, TraitorMessageType messageType)
|
||||
public void SendTraitorMessage(Client client, string message, string missionIdentifier, TraitorMessageType messageType)
|
||||
{
|
||||
if (client == null) { return; }
|
||||
if (!TraitorManager.IsTraitor(client.Character) && client.Connection != OwnerConnection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var msg = new WriteOnlyMessage();
|
||||
var msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ServerPacketHeader.TRAITOR_MESSAGE);
|
||||
msg.Write((byte)messageType);
|
||||
msg.Write(missionIdentifier ?? "");
|
||||
msg.Write(message);
|
||||
|
||||
serverPeer.Send(msg, client.Connection, DeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
@@ -2767,10 +2821,10 @@ namespace Barotrauma.Networking
|
||||
msg.Write((byte)ServerPacketHeader.CHEATS_ENABLED);
|
||||
msg.Write(DebugConsole.CheatsEnabled);
|
||||
msg.WritePadBits();
|
||||
|
||||
|
||||
foreach (Client c in connectedClients)
|
||||
{
|
||||
serverPeer.Send(msg, c.Connection, DeliveryMethod.Reliable);
|
||||
serverPeer.Send(msg, c.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2844,15 +2898,17 @@ namespace Barotrauma.Networking
|
||||
|
||||
List<JobPrefab> jobPreferences = new List<JobPrefab>();
|
||||
int count = message.ReadByte();
|
||||
// TODO: modding support?
|
||||
for (int i = 0; i < Math.Min(count, 3); i++)
|
||||
{
|
||||
string jobIdentifier = message.ReadString();
|
||||
|
||||
JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Identifier == jobIdentifier);
|
||||
if (jobPrefab != null) jobPreferences.Add(jobPrefab);
|
||||
if (JobPrefab.List.TryGetValue(jobIdentifier, out JobPrefab jobPrefab))
|
||||
{
|
||||
jobPreferences.Add(jobPrefab);
|
||||
}
|
||||
}
|
||||
|
||||
sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name);
|
||||
sender.CharacterInfo = new CharacterInfo(Character.HumanSpeciesName, sender.Name);
|
||||
sender.CharacterInfo.RecreateHead(headSpriteId, race, gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
||||
|
||||
//if the client didn't provide job preferences, we'll use the preferences that are randomly assigned in the Client constructor
|
||||
@@ -2865,10 +2921,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void AssignJobs(List<Client> unassigned)
|
||||
{
|
||||
var jobList = JobPrefab.List.Values.ToList();
|
||||
unassigned = new List<Client>(unassigned);
|
||||
|
||||
Dictionary<JobPrefab, int> assignedClientCount = new Dictionary<JobPrefab, int>();
|
||||
foreach (JobPrefab jp in JobPrefab.List)
|
||||
foreach (JobPrefab jp in jobList)
|
||||
{
|
||||
assignedClientCount.Add(jp, 0);
|
||||
}
|
||||
@@ -2916,7 +2973,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
unassignedJobsFound = false;
|
||||
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.List)
|
||||
foreach (JobPrefab jobPrefab in jobList)
|
||||
{
|
||||
if (unassigned.Count == 0) break;
|
||||
if (jobPrefab.MinNumber < 1 || assignedClientCount[jobPrefab] >= jobPrefab.MinNumber) continue;
|
||||
@@ -2954,22 +3011,22 @@ namespace Barotrauma.Networking
|
||||
foreach (Client c in unassigned)
|
||||
{
|
||||
//find all jobs that are still available
|
||||
var remainingJobs = JobPrefab.List.FindAll(jp => assignedClientCount[jp] < jp.MaxNumber && c.Karma >= jp.MinKarma);
|
||||
var remainingJobs = jobList.FindAll(jp => assignedClientCount[jp] < jp.MaxNumber && c.Karma >= jp.MinKarma);
|
||||
|
||||
//all jobs taken, give a random job
|
||||
if (remainingJobs.Count == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
|
||||
int jobIndex = Rand.Range(0, JobPrefab.List.Count);
|
||||
int jobIndex = Rand.Range(0, jobList.Count);
|
||||
int skips = 0;
|
||||
while (c.Karma < JobPrefab.List[jobIndex].MinKarma)
|
||||
while (c.Karma < jobList[jobIndex].MinKarma)
|
||||
{
|
||||
jobIndex++;
|
||||
skips++;
|
||||
if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count;
|
||||
if (skips >= JobPrefab.List.Count) break;
|
||||
if (jobIndex >= jobList.Count) jobIndex -= jobList.Count;
|
||||
if (skips >= jobList.Count) break;
|
||||
}
|
||||
c.AssignedJob = JobPrefab.List[jobIndex];
|
||||
c.AssignedJob = jobList[jobIndex];
|
||||
assignedClientCount[c.AssignedJob]++;
|
||||
}
|
||||
else //some jobs still left, choose one of them by random
|
||||
@@ -2982,12 +3039,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void AssignBotJobs(List<CharacterInfo> bots, Character.TeamType teamID)
|
||||
{
|
||||
var jobList = JobPrefab.List.Values.ToList();
|
||||
Dictionary<JobPrefab, int> assignedPlayerCount = new Dictionary<JobPrefab, int>();
|
||||
foreach (JobPrefab jp in JobPrefab.List)
|
||||
foreach (JobPrefab jp in jobList)
|
||||
{
|
||||
assignedPlayerCount.Add(jp, 0);
|
||||
}
|
||||
|
||||
|
||||
//count the clients who already have characters with an assigned job
|
||||
foreach (Client c in connectedClients)
|
||||
{
|
||||
@@ -3005,7 +3063,7 @@ namespace Barotrauma.Networking
|
||||
List<CharacterInfo> unassignedBots = new List<CharacterInfo>(bots);
|
||||
foreach (CharacterInfo bot in bots)
|
||||
{
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.List)
|
||||
foreach (JobPrefab jobPrefab in jobList)
|
||||
{
|
||||
if (jobPrefab.MinNumber < 1 || assignedPlayerCount[jobPrefab] >= jobPrefab.MinNumber) continue;
|
||||
bot.Job = new Job(jobPrefab);
|
||||
@@ -3019,12 +3077,12 @@ namespace Barotrauma.Networking
|
||||
foreach (CharacterInfo c in unassignedBots)
|
||||
{
|
||||
//find all jobs that are still available
|
||||
var remainingJobs = JobPrefab.List.FindAll(jp => assignedPlayerCount[jp] < jp.MaxNumber);
|
||||
var remainingJobs = jobList.FindAll(jp => assignedPlayerCount[jp] < jp.MaxNumber);
|
||||
//all jobs taken, give a random job
|
||||
if (remainingJobs.Count == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to assign a suitable job for bot \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
|
||||
c.Job = new Job(JobPrefab.List[Rand.Range(0, JobPrefab.List.Count)]);
|
||||
c.Job = Job.Random();
|
||||
assignedPlayerCount[c.Job.Prefab]++;
|
||||
}
|
||||
else //some jobs still left, choose one of them by random
|
||||
@@ -3121,7 +3179,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
partial class PreviousPlayer
|
||||
{
|
||||
public string Name;
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Barotrauma
|
||||
|
||||
private void SendKarmaNotifications(Client client, string debugKarmaChangeReason = "")
|
||||
{
|
||||
//send a notification about karma changing if the karma has changed by x% within the last second
|
||||
//send a notification about karma changing if the karma has changed by x%
|
||||
|
||||
var clientMemory = GetClientMemory(client);
|
||||
float karmaChange = client.Karma - clientMemory.PreviousNotifiedKarma;
|
||||
@@ -110,8 +110,8 @@ namespace Barotrauma
|
||||
{
|
||||
GameMain.Server.SendDirectChatMessage(TextManager.Get(karmaChange < 0 ? "KarmaDecreasedUnknownAmount" : "KarmaIncreasedUnknownAmount"), client);
|
||||
}
|
||||
clientMemory.PreviousNotifiedKarma = client.Karma;
|
||||
}
|
||||
clientMemory.PreviousNotifiedKarma = client.Karma;
|
||||
}
|
||||
|
||||
private void UpdateClient(Client client, float deltaTime)
|
||||
@@ -324,13 +324,12 @@ namespace Barotrauma
|
||||
if (damageAmount > 0)
|
||||
{
|
||||
if (StructureDamageKarmaDecrease <= 0.0f) { return; }
|
||||
|
||||
if (GameMain.Server.TraitorManager?.Traitors != null)
|
||||
{
|
||||
if (GameMain.Server.TraitorManager.Traitors.Any(t =>
|
||||
t.Character == attacker &&
|
||||
t.CurrentObjective != null &&
|
||||
t.CurrentObjective.HasGoalsOfType<Traitor.GoalFloodPercentOfSub>()))
|
||||
if (GameMain.Server.TraitorManager?.Traitors != null)
|
||||
{
|
||||
if (GameMain.Server.TraitorManager.Traitors.Any(t =>
|
||||
t.Character == attacker &&
|
||||
t.CurrentObjective != null &&
|
||||
t.CurrentObjective.IsAllowedToDamage(structure)))
|
||||
{
|
||||
//traitor tasked to flood the sub -> damaging structures is ok
|
||||
return;
|
||||
|
||||
+38
-30
@@ -68,12 +68,14 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (netServer != null) { return; }
|
||||
|
||||
netPeerConfiguration = new NetPeerConfiguration("barotrauma");
|
||||
netPeerConfiguration.AcceptIncomingConnections = true;
|
||||
netPeerConfiguration.AutoExpandMTU = false;
|
||||
netPeerConfiguration.MaximumConnections = serverSettings.MaxPlayers * 2;
|
||||
netPeerConfiguration.EnableUPnP = serverSettings.EnableUPnP;
|
||||
netPeerConfiguration.Port = serverSettings.Port;
|
||||
netPeerConfiguration = new NetPeerConfiguration("barotrauma")
|
||||
{
|
||||
AcceptIncomingConnections = true,
|
||||
AutoExpandMTU = false,
|
||||
MaximumConnections = serverSettings.MaxPlayers * 2,
|
||||
EnableUPnP = serverSettings.EnableUPnP,
|
||||
Port = serverSettings.Port
|
||||
};
|
||||
|
||||
netPeerConfiguration.DisableMessageType(NetIncomingMessageType.DebugMessage |
|
||||
NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt |
|
||||
@@ -96,16 +98,16 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close(string msg=null)
|
||||
public override void Close(string msg = null)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
for (int i=pendingClients.Count-1;i>=0;i--)
|
||||
for (int i = pendingClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
RemovePendingClient(pendingClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
|
||||
RemovePendingClient(pendingClients[i], DisconnectReason.ServerShutdown, msg);
|
||||
}
|
||||
|
||||
for (int i=connectedClients.Count-1;i>=0;i--)
|
||||
for (int i = connectedClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Disconnect(connectedClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
|
||||
}
|
||||
@@ -255,7 +257,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (pendingClient != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.AuthenticationRequired.ToString()+"/ Received data message from unauthenticated client");
|
||||
RemovePendingClient(pendingClient, DisconnectReason.AuthenticationRequired, "Received data message from unauthenticated client");
|
||||
}
|
||||
else if (inc.SenderConnection.Status != NetConnectionStatus.Disconnected &&
|
||||
inc.SenderConnection.Status != NetConnectionStatus.Disconnecting)
|
||||
@@ -307,8 +309,7 @@ namespace Barotrauma.Networking
|
||||
PendingClient pendingClient = pendingClients.Find(c => c.Connection == inc.SenderConnection);
|
||||
if (pendingClient != null)
|
||||
{
|
||||
disconnectMsg = $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}";
|
||||
RemovePendingClient(pendingClient, disconnectMsg);
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -344,7 +345,7 @@ namespace Barotrauma.Networking
|
||||
!IPAddress.IsLoopback(pendingClient.Connection.RemoteEndPoint.Address.MapToIPv4()) &&
|
||||
ownerKey == null || ownKey == 0 && ownKey != ownerKey)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidName.ToString() + "/ The name \"" + name + "\" is invalid");
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -353,7 +354,7 @@ namespace Barotrauma.Networking
|
||||
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
|
||||
if (!isCompatibleVersion)
|
||||
{
|
||||
RemovePendingClient(pendingClient,
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
|
||||
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={version}");
|
||||
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
|
||||
@@ -388,7 +389,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (missingPackages.Count == 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient,
|
||||
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
|
||||
$"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
@@ -397,7 +398,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
List<string> packageStrs = new List<string>();
|
||||
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
|
||||
RemovePendingClient(pendingClient,
|
||||
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
|
||||
$"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
|
||||
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
@@ -423,7 +424,7 @@ namespace Barotrauma.Networking
|
||||
ServerAuth.StartAuthSessionResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
|
||||
if (authSessionStartState != ServerAuth.StartAuthSessionResult.OK)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: " + authSessionStartState.ToString());
|
||||
return;
|
||||
}
|
||||
pendingClient.SteamID = steamId;
|
||||
@@ -436,7 +437,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (pendingClient.SteamID != steamId)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ SteamID mismatch");
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -466,7 +467,7 @@ namespace Barotrauma.Networking
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID.Value, banMsg, null);
|
||||
}
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.Connection.RemoteEndPoint.Address, banMsg, null);
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned.ToString()+" /"+banMsg);
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -497,7 +498,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (serverSettings.BanList.IsBanned(pendingClient.Connection.RemoteEndPoint.Address, pendingClient.SteamID ?? 0))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned.ToString());
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, "");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -505,13 +506,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (connectedClients.Count >= serverSettings.MaxPlayers)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.ServerFull.ToString());
|
||||
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
|
||||
}
|
||||
|
||||
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
|
||||
{
|
||||
LidgrenConnection newConnection = new LidgrenConnection(pendingClient.Name, pendingClient.Connection, pendingClient.SteamID ?? 0);
|
||||
newConnection.Status = NetworkConnectionStatus.Connected;
|
||||
LidgrenConnection newConnection = new LidgrenConnection(pendingClient.Name, pendingClient.Connection, pendingClient.SteamID ?? 0)
|
||||
{
|
||||
Status = NetworkConnectionStatus.Connected
|
||||
};
|
||||
connectedClients.Add(newConnection);
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
@@ -531,7 +534,7 @@ namespace Barotrauma.Networking
|
||||
pendingClient.TimeOut -= deltaTime;
|
||||
if (pendingClient.TimeOut < 0.0)
|
||||
{
|
||||
RemovePendingClient(pendingClient, Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
}
|
||||
|
||||
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
|
||||
@@ -555,7 +558,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Server.SimulatedDuplicatesChance;
|
||||
netPeerConfiguration.SimulatedMinimumLatency = GameMain.Server.SimulatedMinimumLatency;
|
||||
netPeerConfiguration.SimulatedRandomLatency = GameMain.Server.SimulatedRandomLatency;
|
||||
netPeerConfiguration.SimulatedLoss = GameMain.Server.SimulatedLoss;
|
||||
#endif
|
||||
NetSendResult result = netServer.SendMessage(outMsg, pendingClient.Connection, NetDeliveryMethod.ReliableUnordered);
|
||||
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
|
||||
{
|
||||
@@ -564,7 +572,7 @@ namespace Barotrauma.Networking
|
||||
//DebugConsole.NewMessage("sent update to pending client: "+result);
|
||||
}
|
||||
|
||||
private void RemovePendingClient(PendingClient pendingClient, string reason)
|
||||
private void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
@@ -579,7 +587,7 @@ namespace Barotrauma.Networking
|
||||
pendingClient.AuthSessionStarted = false;
|
||||
}
|
||||
|
||||
pendingClient.Connection.Disconnect(reason);
|
||||
pendingClient.Connection.Disconnect(reason + "/" + msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +620,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (serverSettings.BanList.IsBanned(pendingClient.Connection.RemoteEndPoint.Address, steamID))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned.ToString() + "/ SteamID banned");
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, "SteamID banned");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -623,7 +631,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication failed: " + status.ToString());
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam authentication failed: " + status.ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-22
@@ -105,7 +105,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
for (int i = pendingClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
RemovePendingClient(pendingClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
|
||||
RemovePendingClient(pendingClients[i], DisconnectReason.ServerShutdown, msg);
|
||||
}
|
||||
|
||||
for (int i = connectedClients.Count - 1; i >= 0; i--)
|
||||
@@ -244,7 +244,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (pendingClient != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned.ToString()+"/ Banned");
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, "Banned");
|
||||
}
|
||||
else if (connectedClient != null)
|
||||
{
|
||||
@@ -257,7 +257,7 @@ namespace Barotrauma.Networking
|
||||
if (pendingClient != null)
|
||||
{
|
||||
string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}";
|
||||
RemovePendingClient(pendingClient, disconnectMsg);
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, disconnectMsg);
|
||||
}
|
||||
else if (connectedClient != null)
|
||||
{
|
||||
@@ -313,9 +313,11 @@ namespace Barotrauma.Networking
|
||||
if (OwnerConnection == null)
|
||||
{
|
||||
string ownerName = inc.ReadString();
|
||||
OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID);
|
||||
OwnerConnection.Status = NetworkConnectionStatus.Connected;
|
||||
|
||||
OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID)
|
||||
{
|
||||
Status = NetworkConnectionStatus.Connected
|
||||
};
|
||||
|
||||
OnInitializationComplete?.Invoke(OwnerConnection);
|
||||
}
|
||||
return;
|
||||
@@ -384,7 +386,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (!Client.IsValidName(name, serverSettings))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidName.ToString() + "/ The name \"" + name + "\" is invalid");
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -392,7 +394,7 @@ namespace Barotrauma.Networking
|
||||
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
|
||||
if (!isCompatibleVersion)
|
||||
{
|
||||
RemovePendingClient(pendingClient,
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
|
||||
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={version}");
|
||||
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
|
||||
@@ -427,7 +429,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (missingPackages.Count == 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient,
|
||||
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
|
||||
$"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
@@ -436,7 +438,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
List<string> packageStrs = new List<string>();
|
||||
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
|
||||
RemovePendingClient(pendingClient,
|
||||
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
|
||||
$"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
|
||||
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
|
||||
return;
|
||||
@@ -471,7 +473,7 @@ namespace Barotrauma.Networking
|
||||
string banMsg = "Failed to enter correct password too many times";
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID, banMsg, null);
|
||||
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned.ToString()+"/ "+banMsg);
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -502,21 +504,23 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (serverSettings.BanList.IsBanned(pendingClient.SteamID))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned.ToString()+"/ Initialization interrupted by ban");
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, "Initialization interrupted by ban");
|
||||
return;
|
||||
}
|
||||
|
||||
//DebugConsole.NewMessage("pending client status: " + pendingClient.InitializationStep);
|
||||
|
||||
if (connectedClients.Count >= serverSettings.MaxPlayers-1)
|
||||
if (connectedClients.Count >= serverSettings.MaxPlayers - 1)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.ServerFull.ToString());
|
||||
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
|
||||
}
|
||||
|
||||
|
||||
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
|
||||
{
|
||||
SteamP2PConnection newConnection = new SteamP2PConnection(pendingClient.Name, pendingClient.SteamID);
|
||||
newConnection.Status = NetworkConnectionStatus.Connected;
|
||||
SteamP2PConnection newConnection = new SteamP2PConnection(pendingClient.Name, pendingClient.SteamID)
|
||||
{
|
||||
Status = NetworkConnectionStatus.Connected
|
||||
};
|
||||
connectedClients.Add(newConnection);
|
||||
pendingClients.Remove(pendingClient);
|
||||
OnInitializationComplete?.Invoke(newConnection);
|
||||
@@ -525,7 +529,7 @@ namespace Barotrauma.Networking
|
||||
pendingClient.TimeOut -= Timing.Step;
|
||||
if (pendingClient.TimeOut < 0.0)
|
||||
{
|
||||
RemovePendingClient(pendingClient, Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
}
|
||||
|
||||
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
|
||||
@@ -562,13 +566,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private void RemovePendingClient(PendingClient pendingClient, string reason)
|
||||
private void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
if (pendingClients.Contains(pendingClient))
|
||||
{
|
||||
SendDisconnectMessage(pendingClient.SteamID, reason);
|
||||
SendDisconnectMessage(pendingClient.SteamID, reason + "/" + msg);
|
||||
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
@@ -610,9 +614,14 @@ namespace Barotrauma.Networking
|
||||
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableOrdered;
|
||||
break;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Server.SimulatedDuplicatesChance;
|
||||
netPeerConfiguration.SimulatedMinimumLatency = GameMain.Server.SimulatedMinimumLatency;
|
||||
netPeerConfiguration.SimulatedRandomLatency = GameMain.Server.SimulatedRandomLatency;
|
||||
netPeerConfiguration.SimulatedLoss = GameMain.Server.SimulatedLoss;
|
||||
#endif
|
||||
NetOutgoingMessage lidgrenMsg = netServer.CreateMessage();
|
||||
byte[] msgData = new byte[1500];
|
||||
byte[] msgData = new byte[msg.LengthBytes];
|
||||
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
|
||||
lidgrenMsg.Write(conn.SteamID);
|
||||
lidgrenMsg.Write((byte)((isCompressed ? PacketHeader.IsCompressed : PacketHeader.None) | PacketHeader.IsServerMessage));
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Barotrauma.Networking
|
||||
CharacterInfo botToRespawn = existingBots.Find(b => b.IsDead)?.Info;
|
||||
if (botToRespawn == null)
|
||||
{
|
||||
botToRespawn = new CharacterInfo(Character.HumanConfigFile);
|
||||
botToRespawn = new CharacterInfo(Character.HumanSpeciesName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -225,7 +225,7 @@ namespace Barotrauma.Networking
|
||||
//all characters are in Team 1 in game modes/missions with only one team.
|
||||
//if at some point we add a game mode with multiple teams where respawning is possible, this needs to be reworked
|
||||
c.TeamID = Character.TeamType.Team1;
|
||||
if (c.CharacterInfo == null) c.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, c.Name);
|
||||
if (c.CharacterInfo == null) c.CharacterInfo = new CharacterInfo(Character.HumanSpeciesName, c.Name);
|
||||
}
|
||||
List<CharacterInfo> characterInfos = clients.Select(c => c.CharacterInfo).ToList();
|
||||
|
||||
@@ -290,7 +290,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
var oxyTank = new Item(oxyPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(oxyTank, false);
|
||||
divingSuit.Combine(oxyTank);
|
||||
divingSuit.Combine(oxyTank, user: null);
|
||||
respawnItems.Add(oxyTank);
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ namespace Barotrauma.Networking
|
||||
var battery = new Item(batteryPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(battery, false);
|
||||
|
||||
scooter.Combine(battery);
|
||||
scooter.Combine(battery, user: null);
|
||||
respawnItems.Add(scooter);
|
||||
respawnItems.Add(battery);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Barotrauma.Networking
|
||||
Whitelist.ServerAdminWrite(outMsg, c);
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage outMsg,Client c)
|
||||
public void ServerWrite(IWriteMessage outMsg, Client c)
|
||||
{
|
||||
outMsg.Write(ServerName);
|
||||
outMsg.Write(ServerMessageText);
|
||||
@@ -69,8 +69,8 @@ namespace Barotrauma.Networking
|
||||
outMsg.WritePadBits();
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerRead(IReadMessage incMsg,Client c)
|
||||
|
||||
public void ServerRead(IReadMessage incMsg, Client c)
|
||||
{
|
||||
if (!c.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace Barotrauma.Networking
|
||||
if (ServerMessageText != serverMessageText) changed = true;
|
||||
ServerMessageText = serverMessageText;
|
||||
}
|
||||
|
||||
|
||||
if (flags.HasFlag(NetFlags.Properties))
|
||||
{
|
||||
changed |= ReadExtraCargo(incMsg);
|
||||
@@ -169,7 +169,15 @@ namespace Barotrauma.Networking
|
||||
changed |= true;
|
||||
}
|
||||
|
||||
if (changed) GameMain.NetLobbyScreen.LastUpdateID++;
|
||||
if (changed)
|
||||
{
|
||||
if (KarmaPreset == "custom")
|
||||
{
|
||||
GameMain.NetworkMember?.KarmaManager?.SaveCustomPreset();
|
||||
GameMain.NetworkMember?.KarmaManager?.Save();
|
||||
}
|
||||
GameMain.NetLobbyScreen.LastUpdateID++;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveSettings()
|
||||
@@ -220,7 +228,7 @@ namespace Barotrauma.Networking
|
||||
doc = XMLExtensions.TryLoadXml(SettingsFile);
|
||||
}
|
||||
|
||||
if (doc == null || doc.Root == null)
|
||||
if (doc == null)
|
||||
{
|
||||
doc = new XDocument(new XElement("serversettings"));
|
||||
}
|
||||
@@ -249,7 +257,7 @@ namespace Barotrauma.Networking
|
||||
"192-255",
|
||||
"384-591",
|
||||
"1024-1279",
|
||||
"19968-40959","13312-19903","131072-173791","173824-178207","178208-183983","63744-64255","194560-195103" //CJK
|
||||
"19968-40959","13312-19903","131072-15043983","15043985-173791","173824-178207","178208-183983","63744-64255","194560-195103" //CJK
|
||||
};
|
||||
|
||||
string[] allowedClientNameCharsStr = doc.Root.GetAttributeStringArray("AllowedClientNameChars", defaultAllowedClientNameChars);
|
||||
@@ -333,6 +341,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
XDocument doc = XMLExtensions.TryLoadXml(ClientPermissionsFile);
|
||||
if (doc == null) { return; }
|
||||
foreach (XElement clientElement in doc.Root.Elements())
|
||||
{
|
||||
string clientName = clientElement.GetAttributeString("name", "");
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Barotrauma.Steam
|
||||
// These server state variables may be changed at any time. Note that there is no longer a mechanism
|
||||
// to send the player count. The player count is maintained by steam and you should use the player
|
||||
// creation/authentication functions to maintain your player count.
|
||||
instance.server.ServerName = server.Name;
|
||||
instance.server.ServerName = server.ServerName;
|
||||
instance.server.MaxPlayers = server.ServerSettings.MaxPlayers;
|
||||
instance.server.Passworded = server.ServerSettings.HasPassword;
|
||||
instance.server.MapName = GameMain.NetLobbyScreen?.SelectedSub?.DisplayName ?? "";
|
||||
|
||||
@@ -62,6 +62,23 @@ namespace Barotrauma
|
||||
|
||||
static void CrashDump(GameMain game, string filePath, Exception exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
GameMain.Server?.ServerSettings?.SaveSettings();
|
||||
GameMain.Server?.ServerSettings?.BanList.Save();
|
||||
if (GameMain.Server?.ServerSettings?.KarmaPreset == "custom")
|
||||
{
|
||||
GameMain.Server?.KarmaManager?.SaveCustomPreset();
|
||||
GameMain.Server?.KarmaManager?.Save();
|
||||
}
|
||||
}
|
||||
//gotta catch them all, we don't want to crash while writing a crash report
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Exception thrown while writing a crash report: " + e.Message + "\n" + e.StackTrace;
|
||||
GameAnalyticsManager.AddErrorEventOnce("CrashDump:FailedToSaveSettings", EGAErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
|
||||
int existingFiles = 0;
|
||||
string originalFilePath = filePath;
|
||||
while (File.Exists(filePath))
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
{
|
||||
public abstract class Goal
|
||||
{
|
||||
public Traitor Traitor { get; private set; }
|
||||
public HashSet<Traitor> Traitors { get; } = new HashSet<Traitor>();
|
||||
public TraitorMission Mission { get; internal set; }
|
||||
|
||||
public virtual string StatusTextId { get; set; } = "TraitorGoalStatusTextFormat";
|
||||
@@ -21,13 +21,13 @@ namespace Barotrauma
|
||||
public virtual string StatusValueTextId => IsCompleted ? "complete" : "inprogress";
|
||||
|
||||
public virtual IEnumerable<string> StatusTextKeys => new [] { "[infotext]", "[status]" };
|
||||
public virtual IEnumerable<string> StatusTextValues => new [] { InfoText, TextManager.FormatServerMessage(StatusValueTextId) };
|
||||
public virtual IEnumerable<string> StatusTextValues(Traitor traitor) => new [] { InfoText(traitor), TextManager.FormatServerMessage(StatusValueTextId) };
|
||||
|
||||
public virtual IEnumerable<string> InfoTextKeys => new string[] { };
|
||||
public virtual IEnumerable<string> InfoTextValues => new string[] { };
|
||||
public virtual IEnumerable<string> InfoTextValues(Traitor traitor) => new string[] { };
|
||||
|
||||
public virtual IEnumerable<string> CompletedTextKeys => new string[] { };
|
||||
public virtual IEnumerable<string> CompletedTextValues => new string[] { };
|
||||
public virtual IEnumerable<string> CompletedTextValues(Traitor traitor) => new string[] { };
|
||||
|
||||
protected virtual string FormatText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => TextManager.FormatServerMessageWithGenderPronouns(traitor?.Character?.Info?.Gender ?? Gender.None, textId, keys, values);
|
||||
|
||||
@@ -35,20 +35,19 @@ namespace Barotrauma
|
||||
protected internal virtual string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => FormatText(traitor, textId, keys, values);
|
||||
protected internal virtual string GetCompletedText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => FormatText(traitor, textId, keys, values);
|
||||
|
||||
public virtual string StatusText => GetStatusText(Traitor, StatusTextId, StatusTextKeys, StatusTextValues);
|
||||
public virtual string InfoText => GetInfoText(Traitor, InfoTextId, InfoTextKeys, InfoTextValues);
|
||||
public virtual string StatusText(Traitor traitor) => GetStatusText(traitor, StatusTextId, StatusTextKeys, StatusTextValues(traitor));
|
||||
public virtual string InfoText(Traitor traitor) => GetInfoText(traitor, InfoTextId, InfoTextKeys, InfoTextValues(traitor));
|
||||
|
||||
public virtual string CompletedText => CompletedTextId != null ? GetCompletedText(Traitor, CompletedTextId, CompletedTextKeys, CompletedTextValues) : StatusText;
|
||||
public virtual string CompletedText(Traitor traitor) => CompletedTextId != null ? GetCompletedText(traitor, CompletedTextId, CompletedTextKeys, CompletedTextValues(traitor)) : StatusText(traitor);
|
||||
|
||||
public abstract bool IsCompleted { get; }
|
||||
public virtual bool IsStarted => Traitor != null;
|
||||
public virtual bool CanBeCompleted => !(Traitor?.Character?.IsDead ?? true);
|
||||
|
||||
public virtual bool IsStarted(Traitor traitor) => Traitors.Contains(traitor);
|
||||
public virtual bool CanBeCompleted(ICollection<Traitor> traitors) => !Traitors.Any(traitor => traitor.Character?.IsDead ?? true);
|
||||
public virtual bool IsEnemy(Character character) => false;
|
||||
|
||||
public virtual bool IsAllowedToDamage(Structure structure) => false;
|
||||
public virtual bool Start(Traitor traitor)
|
||||
{
|
||||
Traitor = traitor;
|
||||
Traitors.Add(traitor);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma
|
||||
private readonly bool matchInventory;
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[percentage]", "[tag]" });
|
||||
public override IEnumerable<string> InfoTextValues => base.InfoTextValues.Concat(new string[] { string.Format("{0:0}", DestroyPercent * 100.0f), tagPrefabName ?? "" });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { string.Format("{0:0}", DestroyPercent * 100.0f), tagPrefabName ?? "" });
|
||||
|
||||
private readonly float destroyPercent;
|
||||
private float DestroyPercent => destroyPercent;
|
||||
@@ -31,7 +31,7 @@ namespace Barotrauma
|
||||
int result = 0;
|
||||
foreach (var item in Item.ItemList)
|
||||
{
|
||||
if (!matchInventory && item.FindParentInventory(inventory => inventory.Owner is Character && inventory.Owner != Traitor.Character) != null)
|
||||
if (!matchInventory && Traitors.All(traitor => item.FindParentInventory(inventory => inventory.Owner is Character && inventory.Owner != traitor.Character) != null))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.Submarine.TeamID != Traitor.Character.TeamID) { continue; }
|
||||
if (Traitors.All(traitor => item.Submarine.TeamID != traitor.Character.TeamID)) { continue; }
|
||||
}
|
||||
|
||||
if (item.Condition <= 0.0f)
|
||||
|
||||
@@ -24,40 +24,39 @@ namespace Barotrauma
|
||||
private string targetHullNameText;
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[identifier]", "[target]", "[targethullname]" });
|
||||
public override IEnumerable<string> InfoTextValues => base.InfoTextValues.Concat(new string[] { targetNameText ?? "", targetContainerNameText ?? "", targetHullNameText ?? "" });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { targetNameText ?? "", targetContainerNameText ?? "", targetHullNameText ?? "" });
|
||||
|
||||
public override bool IsCompleted => target != null && target.ParentInventory == Traitor.Character.Inventory;
|
||||
public override bool CanBeCompleted {
|
||||
get
|
||||
public override bool IsCompleted => target != null && Traitors.Any(traitor => traitor.Character.HasItem(target));
|
||||
public override bool CanBeCompleted(ICollection<Traitor> traitors)
|
||||
{
|
||||
if (!base.CanBeCompleted(traitors))
|
||||
{
|
||||
if (!base.CanBeCompleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (target == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (target.Removed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (target.Submarine == null)
|
||||
{
|
||||
if (!(target.ParentInventory?.Owner is Character))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target.Submarine.TeamID != Traitor.Character.TeamID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (target == null)
|
||||
{
|
||||
var targetPrefabCandidate = FindItemPrefab(identifier);
|
||||
return targetPrefabCandidate != null && FindTargetContainer(traitors, targetPrefabCandidate) != null;
|
||||
}
|
||||
if (target.Removed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (target.Submarine == null)
|
||||
{
|
||||
if (!(target.ParentInventory?.Owner is Character))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Traitors.All(traitor => target.Submarine.TeamID != traitor.Character.TeamID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool IsEnemy(Character character) => base.IsEnemy(character) || (target != null && target.FindParentInventory(inventory => inventory == character.Inventory) != null);
|
||||
@@ -67,27 +66,51 @@ namespace Barotrauma
|
||||
return (ItemPrefab)MapEntityPrefab.List.Find(prefab => prefab is ItemPrefab && prefab.Identifier == identifier);
|
||||
}
|
||||
|
||||
protected Item FindRandomContainer(bool includeNew, bool includeExisting)
|
||||
protected Item FindRandomContainer(ICollection<Traitor> traitors, ItemPrefab targetPrefabCandidate, bool includeNew, bool includeExisting)
|
||||
{
|
||||
int itemsCount = Item.ItemList.Count;
|
||||
int startIndex = TraitorMission.Random(itemsCount);
|
||||
Item fallback = null;
|
||||
for (int i = 0; i < itemsCount; ++i)
|
||||
List<Item> suitableItems = new List<Item>();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
var item = Item.ItemList[(i + startIndex) % itemsCount];
|
||||
if (item.Submarine == null || item.Submarine.TeamID != Traitor.Character.TeamID)
|
||||
if (item.Submarine == null || traitors.All(traitor => item.Submarine.TeamID != traitor.Character.TeamID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.GetComponent<ItemContainer>() != null && allowedContainerIdentifiers.Contains(item.prefab.Identifier))
|
||||
{
|
||||
if ((includeNew && !item.OwnInventory.IsFull()) || (includeExisting && item.OwnInventory.FindItemByIdentifier(targetPrefab.Identifier) != null))
|
||||
if ((includeNew && !item.OwnInventory.IsFull()) || (includeExisting && item.OwnInventory.FindItemByIdentifier(targetPrefabCandidate.Identifier) != null))
|
||||
{
|
||||
return item;
|
||||
suitableItems.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (suitableItems.Count == 0) { return null; }
|
||||
return suitableItems[TraitorMission.Random(suitableItems.Count)];
|
||||
}
|
||||
|
||||
protected Item FindTargetContainer(ICollection<Traitor> traitors, ItemPrefab targetPrefabCandidate)
|
||||
{
|
||||
Item result = null;
|
||||
if (preferNew)
|
||||
{
|
||||
result = FindRandomContainer(traitors, targetPrefabCandidate, true, false);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = FindRandomContainer(traitors, targetPrefabCandidate, allowNew, allowExisting);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (allowNew && !result.OwnInventory.IsFull())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (allowExisting && result.OwnInventory.FindItemByIdentifier(targetPrefabCandidate.Identifier) != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -97,6 +120,10 @@ namespace Barotrauma
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (targetPrefab != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
targetPrefab = FindItemPrefab(identifier);
|
||||
if (targetPrefab == null)
|
||||
{
|
||||
@@ -104,22 +131,16 @@ namespace Barotrauma
|
||||
}
|
||||
var targetPrefabTextId = targetPrefab.GetItemNameTextId();
|
||||
targetNameText = targetPrefabTextId != null ? TextManager.FormatServerMessage(targetPrefabTextId) : targetPrefab.Name;
|
||||
targetContainer = null;
|
||||
if (preferNew)
|
||||
{
|
||||
targetContainer = FindRandomContainer(true, false);
|
||||
}
|
||||
if (targetContainer == null)
|
||||
{
|
||||
targetContainer = FindRandomContainer(allowNew, allowExisting);
|
||||
}
|
||||
targetContainer = FindTargetContainer(Traitors, targetPrefab);
|
||||
if (targetContainer == null)
|
||||
{
|
||||
targetPrefab = null;
|
||||
targetContainer = null;
|
||||
return false;
|
||||
}
|
||||
var containerPrefabTextId = targetContainer.Prefab.GetItemNameTextId();
|
||||
targetContainerNameText = containerPrefabTextId != null ? TextManager.FormatServerMessage(containerPrefabTextId) : targetContainer.Prefab.Name;
|
||||
var targetHullTextId = targetContainer.CurrentHull != null ? targetContainer.CurrentHull.prefab.GetHullNameTextId() : null;
|
||||
var targetHullTextId = targetContainer.CurrentHull?.prefab.GetHullNameTextId();
|
||||
targetHullNameText = targetHullTextId != null ? TextManager.FormatServerMessage(targetHullTextId) : targetContainer?.CurrentHull?.DisplayName ?? "";
|
||||
if (allowNew && !targetContainer.OwnInventory.IsFull())
|
||||
{
|
||||
@@ -135,6 +156,12 @@ namespace Barotrauma
|
||||
{
|
||||
target = targetContainer.OwnInventory.FindItemByIdentifier(targetPrefab.Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetPrefab = null;
|
||||
targetContainer = null;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma
|
||||
private readonly float minimumFloodingAmount;
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[percentage]" });
|
||||
public override IEnumerable<string> InfoTextValues => base.InfoTextValues.Concat(new string[] { string.Format("{0:0}", minimumFloodingAmount * 100.0f) });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { string.Format("{0:0}", minimumFloodingAmount * 100.0f) });
|
||||
|
||||
private bool isCompleted = false;
|
||||
public override bool IsCompleted => isCompleted;
|
||||
@@ -23,7 +23,7 @@ namespace Barotrauma
|
||||
var floodingAmount = 0.0f;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine == null || hull.Submarine.IsOutpost || hull.Submarine.TeamID != Traitor.Character.TeamID) { continue; }
|
||||
if (hull.Submarine == null || hull.Submarine.IsOutpost || Traitors.All(traitor => hull.Submarine.TeamID != traitor.Character.TeamID)) { continue; }
|
||||
if (hull.Submarine == GameMain.Server?.RespawnManager?.RespawnShuttle) { continue; }
|
||||
++validHullsCount;
|
||||
floodingAmount += hull.WaterVolume / hull.Volume;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma
|
||||
public Character Target { get; private set; }
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[targetname]" });
|
||||
public override IEnumerable<string> InfoTextValues => base.InfoTextValues.Concat(new string[] { Target?.Name ?? "(unknown)" });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { Target?.Name ?? "(unknown)" });
|
||||
|
||||
private bool isCompleted = false;
|
||||
public override bool IsCompleted => isCompleted;
|
||||
|
||||
@@ -14,20 +14,23 @@ namespace Barotrauma
|
||||
private readonly float requiredDistanceSqr;
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[distance]" });
|
||||
public override IEnumerable<string> InfoTextValues => base.InfoTextValues.Concat(new string[] { $"{requiredDistance:0.00}" });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { $"{requiredDistance:0.00}" });
|
||||
|
||||
public override bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Traitor == null || Traitor.Character == null || Traitor.Character.Submarine == null)
|
||||
return Traitors.Any(traitor =>
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var characterPosition = Traitor.Character.WorldPosition;
|
||||
var submarinePosition = Traitor.Character.Submarine.WorldPosition;
|
||||
var distance = Vector2.DistanceSquared(characterPosition, submarinePosition);
|
||||
return distance >= requiredDistanceSqr;
|
||||
if (traitor.Character?.Submarine == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var characterPosition = traitor.Character.WorldPosition;
|
||||
var submarinePosition = traitor.Character.Submarine.WorldPosition;
|
||||
var distance = Vector2.DistanceSquared(characterPosition, submarinePosition);
|
||||
return distance >= requiredDistanceSqr;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Barotrauma
|
||||
public override bool IsCompleted => isCompleted;
|
||||
|
||||
public override IEnumerable<string> StatusTextKeys => base.StatusTextKeys.Concat(new string[] { "[percentage]" });
|
||||
public override IEnumerable<string> StatusTextValues => base.StatusTextValues.Concat(new string[] { string.Format("{0:0}", replaceAmount * 100.0f) });
|
||||
public override IEnumerable<string> StatusTextValues(Traitor traitor) => base.StatusTextValues(traitor).Concat(new string[] { string.Format("{0:0}", replaceAmount * 100.0f) });
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
@@ -25,7 +25,7 @@ namespace Barotrauma
|
||||
int totalAmount = 0, replacedAmount = 0;
|
||||
foreach (var item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine == null || item.Submarine.TeamID != Traitor.Character.TeamID)
|
||||
if (item.Submarine == null || Traitors.All(traitor => item.Submarine.TeamID != traitor.Character.TeamID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma
|
||||
private readonly float conditionThreshold;
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[tag]", "[target]", "[threshold]" });
|
||||
public override IEnumerable<string> InfoTextValues => base.InfoTextValues.Concat(new string[] { tag ?? "", targetItemPrefabName ?? "", string.Format("{0:0}", conditionThreshold) });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { tag ?? "", targetItemPrefabName ?? "", string.Format("{0:0}", conditionThreshold) });
|
||||
|
||||
private bool isCompleted = false;
|
||||
public override bool IsCompleted => isCompleted;
|
||||
@@ -28,7 +28,7 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (var item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine == null || item.Submarine.TeamID != Traitor.Character.TeamID)
|
||||
if (item.Submarine == null || Traitors.All(t => item.Submarine.TeamID != t.Character.TeamID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Traitor
|
||||
{
|
||||
public sealed class GoalWaitForTraitors : Goal
|
||||
{
|
||||
private readonly int requiredCount;
|
||||
private int count = 0;
|
||||
|
||||
public override bool IsCompleted => count >= requiredCount;
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[remaining]", "[count]" });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { $"{requiredCount - count}", $"{requiredCount}" });
|
||||
|
||||
public override bool Start(Traitor traitor)
|
||||
{
|
||||
if (!base.Start(traitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
++count;
|
||||
return true;
|
||||
}
|
||||
|
||||
public GoalWaitForTraitors(int requiredCount) : base()
|
||||
{
|
||||
this.requiredCount = requiredCount;
|
||||
InfoTextId = "TraitorGoalWaitForTraitorsInfoText";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Barotrauma
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Traitor?.Character?.IsHumanoid ?? false;
|
||||
return traitor?.Character?.IsHumanoid ?? false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ namespace Barotrauma
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[duration]" });
|
||||
|
||||
public override IEnumerable<string> InfoTextValues => base.InfoTextValues.Concat(new string[] { $"{TimeSpan.FromSeconds(requiredDuration):g}" });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { requiredDuration.ToString() });
|
||||
|
||||
protected internal override string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values)
|
||||
{
|
||||
var infoText = base.GetInfoText(traitor, textId, keys, values);
|
||||
return !string.IsNullOrEmpty(durationInfoTextId) ? TextManager.FormatServerMessage(durationInfoTextId, new[] { "[infotext]", "[duration]" }, new[] { infoText, $"{TimeSpan.FromSeconds(requiredDuration):g}" }) : infoText;
|
||||
return !string.IsNullOrEmpty(durationInfoTextId) && !infoText.Contains("[duration]") ? TextManager.FormatServerMessage(durationInfoTextId, new[] { "[infotext]", "[duration]" }, new[] { infoText, requiredDuration.ToString() }) : infoText;
|
||||
}
|
||||
|
||||
private bool isCompleted = false;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma
|
||||
private readonly string timeLimitInfoTextId;
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[timelimit]" });
|
||||
public override IEnumerable<string> InfoTextValues => base.InfoTextValues.Concat(new string[] { $"{TimeSpan.FromSeconds(timeLimit):g}" });
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { $"{TimeSpan.FromSeconds(timeLimit):g}" });
|
||||
|
||||
protected internal override string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values)
|
||||
{
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma
|
||||
return !string.IsNullOrEmpty(timeLimitInfoTextId) ? TextManager.FormatServerMessage(timeLimitInfoTextId, new[] { "[infotext]", "[timelimit]" }, new[] { infoText, $"{TimeSpan.FromSeconds(timeLimit):g}" }) : infoText;
|
||||
}
|
||||
|
||||
public override bool CanBeCompleted => base.CanBeCompleted && (!IsStarted || timeRemaining > 0.0f);
|
||||
public override bool CanBeCompleted(ICollection<Traitor> traitors) => base.CanBeCompleted(traitors) && (!Traitors.Any(IsStarted) || timeRemaining > 0.0f);
|
||||
|
||||
private float timeRemaining;
|
||||
|
||||
|
||||
@@ -9,19 +9,17 @@ namespace Barotrauma
|
||||
{
|
||||
private readonly string optionalInfoTextId;
|
||||
|
||||
public override string StatusValueTextId => (base.IsStarted && !base.CanBeCompleted) ? "failed" : base.StatusValueTextId;
|
||||
public override string StatusValueTextId => (Traitors.Any(IsStarted) && !base.CanBeCompleted(Traitors)) ? "failed" : base.StatusValueTextId;
|
||||
|
||||
public override IEnumerable<string> StatusTextValues
|
||||
public override IEnumerable<string> StatusTextValues(Traitor traitor)
|
||||
{
|
||||
get {
|
||||
var values = base.StatusTextValues.ToArray();
|
||||
values[1] = TextManager.GetServerMessage(StatusValueTextId);
|
||||
return values;
|
||||
}
|
||||
var values = base.StatusTextValues(traitor).ToArray();
|
||||
values[1] = TextManager.GetServerMessage(StatusValueTextId);
|
||||
return values;
|
||||
}
|
||||
|
||||
public override bool IsCompleted => base.IsCompleted || (base.IsStarted && !base.CanBeCompleted);
|
||||
public override bool CanBeCompleted => true;
|
||||
public override bool IsCompleted => base.IsCompleted || (Traitors.Any(IsStarted) && !base.CanBeCompleted(Traitors));
|
||||
public override bool CanBeCompleted(ICollection<Traitor> traitors) => true;
|
||||
|
||||
protected internal override string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values)
|
||||
{
|
||||
|
||||
@@ -30,25 +30,25 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public override IEnumerable<string> StatusTextKeys => Goal.StatusTextKeys;
|
||||
public override IEnumerable<string> StatusTextValues => new [] { InfoText, TextManager.FormatServerMessage(StatusValueTextId) };
|
||||
public override IEnumerable<string> StatusTextValues(Traitor traitor) => new [] { InfoText(traitor), TextManager.FormatServerMessage(StatusValueTextId) };
|
||||
|
||||
public override IEnumerable<string> InfoTextKeys => Goal.InfoTextKeys;
|
||||
public override IEnumerable<string> InfoTextValues => Goal.InfoTextValues;
|
||||
public override IEnumerable<string> InfoTextValues(Traitor traitor) => Goal.InfoTextValues(traitor);
|
||||
|
||||
public override IEnumerable<string> CompletedTextKeys => Goal.CompletedTextKeys;
|
||||
public override IEnumerable<string> CompletedTextValues => Goal.CompletedTextValues;
|
||||
public override IEnumerable<string> CompletedTextValues(Traitor traitor) => Goal.CompletedTextValues(traitor);
|
||||
|
||||
protected internal override string GetStatusText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => Goal.GetStatusText(traitor, textId, keys, values);
|
||||
protected internal override string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => Goal.GetInfoText(traitor, textId, keys, values);
|
||||
protected internal override string GetCompletedText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => Goal.GetCompletedText(traitor, textId, keys, values);
|
||||
|
||||
public override string StatusText => GetStatusText(Traitor, StatusTextId, StatusTextKeys, StatusTextValues);
|
||||
public override string InfoText => GetInfoText(Traitor, InfoTextId, InfoTextKeys, InfoTextValues);
|
||||
public override string CompletedText => CompletedTextId != null ? GetCompletedText(Traitor, CompletedTextId, CompletedTextKeys, CompletedTextValues) : StatusText;
|
||||
public override string StatusText(Traitor traitor) => GetStatusText(traitor, StatusTextId, StatusTextKeys, StatusTextValues(traitor));
|
||||
public override string InfoText(Traitor traitor) => GetInfoText(traitor, InfoTextId, InfoTextKeys, InfoTextValues(traitor));
|
||||
public override string CompletedText(Traitor traitor) => CompletedTextId != null ? GetCompletedText(traitor, CompletedTextId, CompletedTextKeys, CompletedTextValues(traitor)) : StatusText(traitor);
|
||||
|
||||
public override bool IsCompleted => Goal.IsCompleted;
|
||||
public override bool IsStarted => base.IsStarted && Goal.IsStarted;
|
||||
public override bool CanBeCompleted => base.CanBeCompleted && Goal.CanBeCompleted;
|
||||
public override bool IsStarted(Traitor traitor) => base.IsStarted(traitor) && Goal.IsStarted(traitor);
|
||||
public override bool CanBeCompleted(ICollection<Traitor> traitors) => base.CanBeCompleted(traitors) && Goal.CanBeCompleted(traitors);
|
||||
|
||||
public override bool IsEnemy(Character character) => base.IsEnemy(character) || Goal.IsEnemy(character);
|
||||
|
||||
|
||||
@@ -21,9 +21,13 @@ namespace Barotrauma
|
||||
public bool IsCompleted => pendingGoals.Count <= 0;
|
||||
public bool IsPartiallyCompleted => completedGoals.Count > 0;
|
||||
public bool IsStarted { get; private set; } = false;
|
||||
public bool CanBeCompleted => !IsStarted || pendingGoals.All(goal => goal.CanBeCompleted);
|
||||
public bool CanBeStarted(ICollection<Traitor> traitors) => !IsStarted && allGoals.Any(goal => goal.CanBeCompleted(traitors));
|
||||
public bool CanBeCompleted => !IsStarted || pendingGoals.All(goal => goal.CanBeCompleted(goal.Traitors));
|
||||
|
||||
public bool IsEnemy(Character character) => pendingGoals.Any(goal => goal.IsEnemy(character));
|
||||
public bool IsAllowedToDamage(Structure structure) => pendingGoals.Any(goal => goal.IsAllowedToDamage(structure));
|
||||
|
||||
public readonly HashSet<string> Roles = new HashSet<string>();
|
||||
|
||||
public string InfoText { get; private set; }
|
||||
|
||||
@@ -33,7 +37,7 @@ namespace Barotrauma
|
||||
string.Join("/",
|
||||
string.Join("/", activeGoals.Select((goal, index) =>
|
||||
{
|
||||
var statusText = goal.StatusText;
|
||||
var statusText = goal.StatusText(Traitor);
|
||||
var startIndex = statusText.LastIndexOf('/') + 1;
|
||||
return $"{statusText.Substring(0, startIndex)}[{index}.st]={statusText.Substring(startIndex)}/[{index}.sl]={TextManager.FormatServerMessage(GoalInfoFormatId, new string[] { "[statustext]" }, new string[] { $"[{index}.st]" })}";
|
||||
}).ToArray()),
|
||||
@@ -43,7 +47,7 @@ namespace Barotrauma
|
||||
string.Join("/",
|
||||
string.Join("/", allGoals.Select((goal, index) =>
|
||||
{
|
||||
var statusText = goal.StatusText;
|
||||
var statusText = goal.StatusText(Traitor);
|
||||
var startIndex = statusText.LastIndexOf('/') + 1;
|
||||
return $"{statusText.Substring(0, startIndex)}[{index}.st]={statusText.Substring(startIndex)}/[{index}.sl]={TextManager.FormatServerMessage(GoalInfoFormatId, new string[] { "[statustext]" }, new string[] { $"[{index}.st]" })}";
|
||||
}).ToArray()),
|
||||
@@ -127,28 +131,21 @@ namespace Barotrauma
|
||||
}
|
||||
IsStarted = true;
|
||||
|
||||
traitor.SendChatMessageBox(StartMessageText);
|
||||
traitor.UpdateCurrentObjective(GoalInfos);
|
||||
traitor.SendChatMessageBox(StartMessageText, traitor.Mission?.Identifier);
|
||||
traitor.UpdateCurrentObjective(GoalInfos, traitor.Mission?.Identifier);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void StartMessage()
|
||||
{
|
||||
Traitor.SendChatMessage(StartMessageText);
|
||||
}
|
||||
|
||||
public void End(bool displayMessage)
|
||||
{
|
||||
if (displayMessage)
|
||||
{
|
||||
Traitor.SendChatMessageBox(EndMessageText);
|
||||
}
|
||||
Traitor.SendChatMessage(StartMessageText, Traitor.Mission?.Identifier);
|
||||
}
|
||||
|
||||
public void EndMessage()
|
||||
{
|
||||
Traitor.SendChatMessage(EndMessageText);
|
||||
Traitor.SendChatMessageBox(EndMessageText, Traitor.Mission?.Identifier);
|
||||
Traitor.SendChatMessage(EndMessageText, Traitor.Mission?.Identifier);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
@@ -171,28 +168,24 @@ namespace Barotrauma
|
||||
pendingGoals.RemoveAt(i);
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
Traitor.SendChatMessage(goal.CompletedText);
|
||||
Traitor.SendChatMessage(goal.CompletedText(Traitor), Traitor.Mission?.Identifier);
|
||||
if (pendingGoals.Count > 0)
|
||||
{
|
||||
Traitor.SendChatMessageBox(goal.CompletedText);
|
||||
Traitor.SendChatMessageBox(goal.CompletedText(Traitor), Traitor.Mission?.Identifier);
|
||||
}
|
||||
Traitor.UpdateCurrentObjective(GoalInfos);
|
||||
Traitor.UpdateCurrentObjective(GoalInfos, Traitor.Mission?.Identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Objective(string infoText, int shuffleGoalsCount, params Goal[] goals)
|
||||
public Objective(string infoText, int shuffleGoalsCount, ICollection<string> roles, ICollection<Goal> goals)
|
||||
{
|
||||
InfoText = infoText;
|
||||
this.shuffleGoalsCount = shuffleGoalsCount;
|
||||
Roles.UnionWith(roles);
|
||||
allGoals.AddRange(goals);
|
||||
}
|
||||
|
||||
public bool HasGoalsOfType<T>() where T : Goal
|
||||
{
|
||||
return allGoals?.Any(g => g is T) ?? false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using Lidgren.Network;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -9,8 +6,8 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly Character Character;
|
||||
|
||||
public string Role { get; private set; }
|
||||
public TraitorMission Mission { get; private set; }
|
||||
public string Role { get; }
|
||||
public TraitorMission Mission { get; }
|
||||
public Objective CurrentObjective => Mission.GetCurrentObjective(this);
|
||||
|
||||
public Traitor(TraitorMission mission, string role, Character character)
|
||||
@@ -30,37 +27,32 @@ namespace Barotrauma
|
||||
}, new string[] {
|
||||
codeWords, codeResponse
|
||||
});
|
||||
|
||||
messageSender(greetingMessage);
|
||||
// boxSender(greetingMessage);
|
||||
// SendChatMessage(greetingMessage);
|
||||
// SendChatMessageBox(greetingMessage);
|
||||
|
||||
Client traitorClient = server.ConnectedClients.Find(c => c.Character == Character);
|
||||
Client ownerClient = server.ConnectedClients.Find(c => c.Connection == server.OwnerConnection);
|
||||
if (traitorClient != ownerClient && ownerClient != null && ownerClient.Character == null)
|
||||
{
|
||||
GameMain.Server.SendTraitorMessage(ownerClient, CurrentObjective.StartMessageServerText, TraitorMessageType.ServerMessageBox);
|
||||
GameMain.Server.SendTraitorMessage(ownerClient, CurrentObjective.StartMessageServerText, Mission?.Identifier, TraitorMessageType.ServerMessageBox);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendChatMessage(string serverText)
|
||||
public void SendChatMessage(string serverText, string iconIdentifier)
|
||||
{
|
||||
Client traitorClient = GameMain.Server.ConnectedClients.Find(c => c.Character == Character);
|
||||
GameMain.Server.SendTraitorMessage(traitorClient, serverText, TraitorMessageType.Server);
|
||||
GameMain.Server.SendTraitorMessage(traitorClient, serverText, iconIdentifier, TraitorMessageType.Server);
|
||||
}
|
||||
|
||||
public void SendChatMessageBox(string serverText)
|
||||
public void SendChatMessageBox(string serverText, string iconIdentifier)
|
||||
{
|
||||
Client traitorClient = GameMain.Server.ConnectedClients.Find(c => c.Character == Character);
|
||||
GameMain.Server.SendTraitorMessage(traitorClient, serverText, TraitorMessageType.ServerMessageBox);
|
||||
GameMain.Server.SendTraitorMessage(traitorClient, serverText, iconIdentifier, TraitorMessageType.ServerMessageBox);
|
||||
}
|
||||
|
||||
public void UpdateCurrentObjective(string objectiveText)
|
||||
public void UpdateCurrentObjective(string objectiveText, string iconIdentifier)
|
||||
{
|
||||
Client traitorClient = GameMain.Server.ConnectedClients.Find(c => c.Character == Character);
|
||||
Character.TraitorCurrentObjective = objectiveText;
|
||||
GameMain.Server.SendTraitorMessage(traitorClient, Character.TraitorCurrentObjective, TraitorMessageType.Objective);
|
||||
GameMain.Server.SendTraitorMessage(traitorClient, Character.TraitorCurrentObjective, iconIdentifier, TraitorMessageType.Objective);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,12 @@ namespace Barotrauma
|
||||
private readonly Dictionary<ulong, int> traitorCountsBySteamId = new Dictionary<ulong, int>();
|
||||
private readonly Dictionary<string, int> traitorCountsByEndPoint = new Dictionary<string, int>();
|
||||
|
||||
public bool ShouldEndRound
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int GetTraitorCount(Tuple<ulong, string> steamIdAndEndPoint)
|
||||
{
|
||||
if (steamIdAndEndPoint.Item1 > 0 && traitorCountsBySteamId.TryGetValue(steamIdAndEndPoint.Item1, out var steamIdResult))
|
||||
@@ -51,6 +57,16 @@ namespace Barotrauma
|
||||
return Traitors.Any(traitor => traitor.Character == character);
|
||||
}
|
||||
|
||||
public string GetTraitorRole(Character character)
|
||||
{
|
||||
var traitor = Traitors.FirstOrDefault(candidate => candidate.Character == character);
|
||||
if (traitor == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return traitor.Role;
|
||||
}
|
||||
|
||||
public TraitorManager()
|
||||
{
|
||||
}
|
||||
@@ -60,18 +76,21 @@ namespace Barotrauma
|
||||
#if DISABLE_MISSIONS
|
||||
return;
|
||||
#endif
|
||||
if (server == null) return;
|
||||
if (server == null) { return; }
|
||||
|
||||
ShouldEndRound = false;
|
||||
|
||||
Traitor.TraitorMission.InitializeRandom();
|
||||
this.server = server;
|
||||
//TODO: configure countdowns in xml
|
||||
startCountdown = MathHelper.Lerp(90.0f, 180.0f, (float)Traitor.TraitorMission.RandomDouble());
|
||||
startCountdown = MathHelper.Lerp(server.ServerSettings.TraitorsMinStartDelay, server.ServerSettings.TraitorsMaxStartDelay, (float)Traitor.TraitorMission.RandomDouble());
|
||||
traitorCountsBySteamId.Clear();
|
||||
traitorCountsByEndPoint.Clear();
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (ShouldEndRound) { return; }
|
||||
|
||||
#if DISABLE_MISSIONS
|
||||
return;
|
||||
#endif
|
||||
@@ -102,21 +121,20 @@ namespace Barotrauma
|
||||
missionCompleted = true;
|
||||
foreach (var traitor in mission.Value.Traitors.Values)
|
||||
{
|
||||
traitor.UpdateCurrentObjective("");
|
||||
traitor.UpdateCurrentObjective("", mission.Value.Identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gameShouldEnd)
|
||||
{
|
||||
GameMain.GameSession.WinningTeam = winningTeam;
|
||||
GameMain.Server.EndGame();
|
||||
ShouldEndRound = true;
|
||||
return;
|
||||
}
|
||||
if (missionCompleted)
|
||||
{
|
||||
Missions.Clear();
|
||||
//TODO: configure countdowns in xml
|
||||
startCountdown = MathHelper.Lerp(90.0f, 180.0f, (float)Traitor.TraitorMission.RandomDouble());
|
||||
startCountdown = MathHelper.Lerp(server.ServerSettings.TraitorsMinRestartDelay, server.ServerSettings.TraitorsMaxRestartDelay, (float)Traitor.TraitorMission.RandomDouble());
|
||||
}
|
||||
}
|
||||
else if (startCountdown > 0.0f && server.GameStarted)
|
||||
@@ -127,7 +145,7 @@ namespace Barotrauma
|
||||
int playerCharactersCount = server.ConnectedClients.Sum(client => client.Character != null && !client.Character.IsDead ? 1 : 0);
|
||||
if (playerCharactersCount < server.ServerSettings.TraitorsMinPlayerCount)
|
||||
{
|
||||
startCountdown = 60.0f;
|
||||
startCountdown = MathHelper.Lerp(server.ServerSettings.TraitorsMinRestartDelay, server.ServerSettings.TraitorsMaxRestartDelay, (float)Traitor.TraitorMission.RandomDouble());
|
||||
return;
|
||||
}
|
||||
if (GameMain.GameSession.Mission is CombatMission)
|
||||
@@ -141,10 +159,10 @@ namespace Barotrauma
|
||||
Missions.Add(teamId, mission);
|
||||
}
|
||||
}
|
||||
var canBeStartedCount = Missions.Sum(mission => mission.Value.CanBeStarted(server, this, mission.Key, "traitor") ? 1 : 0);
|
||||
var canBeStartedCount = Missions.Sum(mission => mission.Value.CanBeStarted(server, this, mission.Key) ? 1 : 0);
|
||||
if (canBeStartedCount >= Missions.Count)
|
||||
{
|
||||
var startSuccessCount = Missions.Sum(mission => mission.Value.Start(server, this, mission.Key, "traitor") ? 1 : 0);
|
||||
var startSuccessCount = Missions.Sum(mission => mission.Value.Start(server, this, mission.Key) ? 1 : 0);
|
||||
if (startSuccessCount >= Missions.Count)
|
||||
{
|
||||
return;
|
||||
@@ -155,9 +173,9 @@ namespace Barotrauma
|
||||
{
|
||||
var mission = TraitorMissionPrefab.RandomPrefab()?.Instantiate();
|
||||
if (mission != null) {
|
||||
if (mission.CanBeStarted(server, this, Character.TeamType.None, "traitor"))
|
||||
if (mission.CanBeStarted(server, this, Character.TeamType.None))
|
||||
{
|
||||
if (mission.Start(server, this, Character.TeamType.None, "traitor"))
|
||||
if (mission.Start(server, this, Character.TeamType.None))
|
||||
{
|
||||
Missions.Add(Character.TeamType.None, mission);
|
||||
return;
|
||||
@@ -166,7 +184,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
Missions.Clear();
|
||||
startCountdown = 60.0f;
|
||||
startCountdown = MathHelper.Lerp(server.ServerSettings.TraitorsMinRestartDelay, server.ServerSettings.TraitorsMaxRestartDelay, (float)Traitor.TraitorMission.RandomDouble());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,23 +196,31 @@ namespace Barotrauma
|
||||
#endif
|
||||
if (GameMain.Server == null || !Missions.Any()) return "";
|
||||
|
||||
return string.Join("\n\n", Missions.Select(mission => mission.Value.GlobalEndMessage));
|
||||
return TextManager.JoinServerMessages("\n\n", Missions.Select(mission => mission.Value.GlobalEndMessage).ToArray());
|
||||
}
|
||||
|
||||
public static T WeightedRandom<T>(ICollection<T> collection, Func<int, int> random, Func<T, int> readSelectedWeight, Action<T, int> writeSelectedWeight, int entryWeight, int selectionWeight) where T : class
|
||||
public static T WeightedRandom<T>(IList<T> collection, int startIndex, int count, Func<int, int> random, Func<T, int> readSelectedWeight, Action<T, int> writeSelectedWeight, int entryWeight, int selectionWeight) where T : class
|
||||
{
|
||||
var count = collection.Count;
|
||||
if (count <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var maxCount = entryWeight + collection.Max(readSelectedWeight);
|
||||
var totalWeight = collection.Sum(entry => maxCount - readSelectedWeight(entry));
|
||||
var selected = random(totalWeight);
|
||||
foreach (var entry in collection)
|
||||
var maxWeight = readSelectedWeight(collection[startIndex]);
|
||||
var totalWeight = entryWeight + maxWeight;
|
||||
for (var i = 1; i < count; ++i)
|
||||
{
|
||||
var weight = readSelectedWeight(collection[startIndex + i]);
|
||||
maxWeight = Math.Max(maxWeight, weight);
|
||||
totalWeight += weight;
|
||||
}
|
||||
maxWeight += entryWeight;
|
||||
totalWeight = count * maxWeight - totalWeight;
|
||||
var selected = random(totalWeight);
|
||||
for(var i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = collection[startIndex + i];
|
||||
var weight = readSelectedWeight(entry);
|
||||
selected -= maxCount;
|
||||
selected -= maxWeight;
|
||||
selected += weight;
|
||||
if (selected <= 0)
|
||||
{
|
||||
@@ -204,5 +230,10 @@ namespace Barotrauma
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static T WeightedRandom<T>(IList<T> collection, Func<int, int> random, Func<T, int> readSelectedWeight, Action<T, int> writeSelectedWeight, int entryWeight, int selectionWeight) where T : class
|
||||
{
|
||||
return WeightedRandom<T>(collection, 0, collection.Count, random, readSelectedWeight, writeSelectedWeight, entryWeight, selectionWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//#define SERVER_IS_TRAITOR
|
||||
//#define ALLOW_SOLO_TRAITOR
|
||||
//#define ALLOW_SOLO_TRAITOR
|
||||
//#define ALLOW_NONHUMANOID_TRAITOR
|
||||
|
||||
using System;
|
||||
using Barotrauma.Networking;
|
||||
@@ -7,6 +7,7 @@ using Lidgren.Network;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -35,28 +36,12 @@ namespace Barotrauma
|
||||
|
||||
public readonly Dictionary<string, Traitor> Traitors = new Dictionary<string, Traitor>();
|
||||
|
||||
public delegate bool RoleFilter(Character character);
|
||||
public readonly Dictionary<string, RoleFilter> Roles = new Dictionary<string, RoleFilter>();
|
||||
|
||||
public string StartText { get; private set; }
|
||||
public string CodeWords { get; private set; }
|
||||
public string CodeResponse { get; private set; }
|
||||
public string EndMessage {
|
||||
get
|
||||
{
|
||||
if (!Traitors.TryGetValue("traitor", out Traitor traitor))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (pendingObjectives.Count <= 0)
|
||||
{
|
||||
if (completedObjectives.Count <= 0) return "";
|
||||
return completedObjectives[completedObjectives.Count - 1].EndMessageText;
|
||||
}
|
||||
else
|
||||
{
|
||||
return pendingObjectives[0].EndMessageText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GlobalEndMessageSuccessTextId { get; private set; }
|
||||
public string GlobalEndMessageSuccessDeadTextId { get; private set; }
|
||||
@@ -65,14 +50,14 @@ namespace Barotrauma
|
||||
public string GlobalEndMessageFailureDeadTextId { get; private set; }
|
||||
public string GlobalEndMessageFailureDetainedTextId { get; private set; }
|
||||
|
||||
private readonly string objectiveGoalInfoFormat = "[index]. [goalinfos]\n";
|
||||
public readonly string Identifier;
|
||||
|
||||
public virtual IEnumerable<string> GlobalEndMessageKeys => new string[] { "[traitorname]", "[traitorgoalinfos]" };
|
||||
public virtual IEnumerable<string> GlobalEndMessageValues {
|
||||
get {
|
||||
var isSuccess = completedObjectives.Count >= allObjectives.Count;
|
||||
return new string[] {
|
||||
(Traitors.TryGetValue("traitor", out var traitor) ? traitor.Character?.Name : null) ?? "(unknown)",
|
||||
string.Join(", ", Traitors.Values.Select(traitor => traitor.Character?.Name ?? "(unknown)")),
|
||||
(isSuccess ? completedObjectives.LastOrDefault() : pendingObjectives.FirstOrDefault())?.GoalInfos ?? ""
|
||||
};
|
||||
}
|
||||
@@ -82,20 +67,19 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Traitors.TryGetValue("traitor", out Traitor traitor))
|
||||
if (Traitors.Any() && allObjectives.Count > 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (allObjectives.Count > 0)
|
||||
{
|
||||
var isSuccess = completedObjectives.Count >= allObjectives.Count;
|
||||
var traitorIsDead = traitor.Character.IsDead;
|
||||
var traitorIsDetained = traitor.Character.LockHands;
|
||||
var messageId = isSuccess
|
||||
? (traitorIsDead ? GlobalEndMessageSuccessDeadTextId : traitorIsDetained ? GlobalEndMessageSuccessDetainedTextId : GlobalEndMessageSuccessTextId)
|
||||
: (traitorIsDead ? GlobalEndMessageFailureDeadTextId : traitorIsDetained ? GlobalEndMessageFailureDetainedTextId : GlobalEndMessageFailureTextId);
|
||||
return TextManager.FormatServerMessageWithGenderPronouns(traitor.Character?.Info?.Gender ?? Gender.None, messageId, GlobalEndMessageKeys.ToArray(), GlobalEndMessageValues.ToArray());
|
||||
return TextManager.JoinServerMessages("\n",
|
||||
Traitors.Values.Select(traitor =>
|
||||
{
|
||||
var isSuccess = completedObjectives.Count >= allObjectives.Count;
|
||||
var traitorIsDead = traitor.Character.IsDead;
|
||||
var traitorIsDetained = traitor.Character.LockHands;
|
||||
var messageId = isSuccess
|
||||
? (traitorIsDead ? GlobalEndMessageSuccessDeadTextId : traitorIsDetained ? GlobalEndMessageSuccessDetainedTextId : GlobalEndMessageSuccessTextId)
|
||||
: (traitorIsDead ? GlobalEndMessageFailureDeadTextId : traitorIsDetained ? GlobalEndMessageFailureDetainedTextId : GlobalEndMessageFailureTextId);
|
||||
return TextManager.FormatServerMessageWithGenderPronouns(traitor.Character?.Info?.Gender ?? Gender.None, messageId, GlobalEndMessageKeys.ToArray(), GlobalEndMessageValues.ToArray());
|
||||
}).ToArray());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -103,21 +87,27 @@ namespace Barotrauma
|
||||
|
||||
public Objective GetCurrentObjective(Traitor traitor)
|
||||
{
|
||||
return pendingObjectives.Count > 0 ? pendingObjectives[0] : null;
|
||||
if (!Traitors.ContainsValue(traitor) || pendingObjectives.Count <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return pendingObjectives.Find(objective => objective.Roles.Contains(traitor.Role));
|
||||
}
|
||||
|
||||
protected List<Tuple<Client, Character>> FindTraitorCandidates(GameServer server, Character.TeamType team, params string[] traitorRoles)
|
||||
protected List<Tuple<Client, Character>> FindTraitorCandidates(GameServer server, Character.TeamType team, RoleFilter traitorRoleFilter)
|
||||
{
|
||||
var traitorCandidates = new List<Tuple<Client, Character>>();
|
||||
#if SERVER_IS_TRAITOR
|
||||
if (server.Character != null)
|
||||
foreach (Client c in server.ConnectedClients)
|
||||
{
|
||||
traitorCandidates.Add(server.Character);
|
||||
}
|
||||
else
|
||||
if (c.Character == null || c.Character.IsDead || c.Character.Removed || !traitorRoleFilter(c.Character) ||
|
||||
(team != Character.TeamType.None && c.Character.TeamID != team))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#if !ALLOW_NONHUMANOID_TRAITOR
|
||||
if (!c.Character.IsHumanoid) { continue; }
|
||||
#endif
|
||||
{
|
||||
traitorCandidates.AddRange(server.ConnectedClients.FindAll(c => c.Character != null && !c.Character.IsDead && (team == Character.TeamType.None || c.Character.TeamID == team)).ConvertAll(client => Tuple.Create(client, client.Character)));
|
||||
traitorCandidates.Add(Tuple.Create(c, c.Character));
|
||||
}
|
||||
return traitorCandidates;
|
||||
}
|
||||
@@ -132,73 +122,127 @@ namespace Barotrauma
|
||||
return characters;
|
||||
}
|
||||
|
||||
public virtual bool CanBeStarted(GameServer server, TraitorManager traitorManager, Character.TeamType team, params string[] traitorRoles)
|
||||
{
|
||||
var traitorCandidates = FindTraitorCandidates(server, team, traitorRoles);
|
||||
if (traitorCandidates.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var characters = FindCharacters();
|
||||
#if !ALLOW_SOLO_TRAITOR
|
||||
if (characters.Count < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool Start(GameServer server, TraitorManager traitorManager, Character.TeamType team, params string[] traitorRoles)
|
||||
protected List<Tuple<string, Tuple<Client, Character>>> AssignTraitors(GameServer server, TraitorManager traitorManager, Character.TeamType team)
|
||||
{
|
||||
List<Character> characters = FindCharacters();
|
||||
List<Tuple<Client, Character>> traitorCandidates = FindTraitorCandidates(server, team, traitorRoles);
|
||||
if (traitorCandidates.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#if !ALLOW_SOLO_TRAITOR
|
||||
if (characters.Count < 2)
|
||||
{
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
CodeWords = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
|
||||
CodeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
|
||||
Traitors.Clear();
|
||||
foreach (var role in traitorRoles)
|
||||
var roleCandidates = new Dictionary<string, HashSet<Tuple<Client, Character>>>();
|
||||
foreach (var role in Roles)
|
||||
{
|
||||
var candidate = TraitorManager.WeightedRandom(traitorCandidates, Random, t =>
|
||||
roleCandidates.Add(role.Key, new HashSet<Tuple<Client, Character>>(FindTraitorCandidates(server, team, role.Value)));
|
||||
if (roleCandidates[role.Key].Count <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
var candidateRoleCounts = new Dictionary<Tuple<Client, Character>, int>();
|
||||
foreach (var candidateEntry in roleCandidates)
|
||||
{
|
||||
foreach (var candidate in candidateEntry.Value)
|
||||
{
|
||||
candidateRoleCounts[candidate] = candidateRoleCounts.TryGetValue(candidate, out var count) ? count + 1 : 1;
|
||||
}
|
||||
}
|
||||
var unassignedRoles = new List<string>(roleCandidates.Keys);
|
||||
unassignedRoles.Sort((a, b) => roleCandidates[a].Count - roleCandidates[b].Count);
|
||||
var assignedCandidates = new List<Tuple<string, Tuple<Client, Character>>>();
|
||||
while (unassignedRoles.Count > 0)
|
||||
{
|
||||
var currentRole = unassignedRoles[0];
|
||||
var availableCandidates = roleCandidates[currentRole].ToList();
|
||||
if (availableCandidates.Count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
unassignedRoles.RemoveAt(0);
|
||||
availableCandidates.Sort((a, b) => candidateRoleCounts[b] - candidateRoleCounts[a]);
|
||||
unassignedRoles.Sort((a, b) => roleCandidates[a].Count - roleCandidates[b].Count);
|
||||
|
||||
int numCandidates = 1;
|
||||
for (int i = 1; i < availableCandidates.Count && candidateRoleCounts[availableCandidates[i]] == candidateRoleCounts[availableCandidates[0]]; ++i)
|
||||
{
|
||||
++numCandidates;
|
||||
}
|
||||
var selected = TraitorManager.WeightedRandom(availableCandidates, 0, numCandidates, Random, t =>
|
||||
{
|
||||
var previousClient = server.FindPreviousClientData(t.Item1);
|
||||
return Math.Max(
|
||||
previousClient != null ? traitorManager.GetTraitorCount(previousClient) : 0,
|
||||
traitorManager.GetTraitorCount(Tuple.Create(t.Item1.SteamID, t.Item1.Connection?.EndPointString ?? "")));
|
||||
}, (t, c) =>
|
||||
{
|
||||
traitorManager.SetTraitorCount(Tuple.Create(t.Item1.SteamID, t.Item1.Connection?.EndPointString ?? ""), c);
|
||||
}, 2, 3);
|
||||
traitorCandidates.Remove(candidate);
|
||||
}, (t, c) => { traitorManager.SetTraitorCount(Tuple.Create(t.Item1.SteamID, t.Item1.Connection?.EndPointString ?? ""), c); }, 2, 3);
|
||||
|
||||
var traitor = new Traitor(this, role, candidate.Item2);
|
||||
Traitors.Add(role, traitor);
|
||||
assignedCandidates.Add(Tuple.Create(currentRole, selected));
|
||||
foreach (var candidate in roleCandidates.Values)
|
||||
{
|
||||
candidate.Remove(selected);
|
||||
}
|
||||
}
|
||||
if (unassignedRoles.Count > 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return assignedCandidates;
|
||||
}
|
||||
|
||||
public virtual bool CanBeStarted(GameServer server, TraitorManager traitorManager, Character.TeamType team)
|
||||
{
|
||||
foreach (var role in Roles)
|
||||
{
|
||||
var candidates = FindTraitorCandidates(server, team, role.Value);
|
||||
if (candidates.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return AssignTraitors(server, traitorManager, team) != null;
|
||||
}
|
||||
|
||||
public virtual bool Start(GameServer server, TraitorManager traitorManager, Character.TeamType team)
|
||||
{
|
||||
var assignedCandidates = AssignTraitors(server, traitorManager, team);
|
||||
if (assignedCandidates == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var messages = new Dictionary<Traitor, List<string>>();
|
||||
Traitors.Clear();
|
||||
foreach (var candidate in assignedCandidates)
|
||||
{
|
||||
var traitor = new Traitor(this, candidate.Item1, candidate.Item2.Item1.Character);
|
||||
Traitors.Add(candidate.Item1, traitor);
|
||||
}
|
||||
CodeWords = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
|
||||
CodeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
|
||||
|
||||
if (pendingObjectives.Count <= 0 || !pendingObjectives[0].CanBeStarted(Traitors.Values))
|
||||
{
|
||||
Traitors.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
var pendingMessages = new Dictionary<Traitor, List<string>>();
|
||||
pendingMessages.Clear();
|
||||
foreach (var traitor in Traitors.Values)
|
||||
{
|
||||
messages[traitor] = new List<string>();
|
||||
if (traitor.CurrentObjective == null) { continue; }
|
||||
traitor.Greet(server, CodeWords, CodeResponse, message => messages[traitor].Add(message));
|
||||
pendingMessages.Add(traitor, new List<string>());
|
||||
}
|
||||
foreach (var traitor in Traitors.Values)
|
||||
{
|
||||
traitor.Greet(server, CodeWords, CodeResponse, message => pendingMessages[traitor].Add(message));
|
||||
}
|
||||
pendingMessages.ForEach(traitor => traitor.Value.ForEach(message => traitor.Key.SendChatMessage(message, Identifier)));
|
||||
pendingMessages.ForEach(traitor => traitor.Value.ForEach(message => traitor.Key.SendChatMessageBox(message, Identifier)));
|
||||
|
||||
messages.ForEach(traitor => traitor.Value.ForEach(message => traitor.Key.SendChatMessage(message)));
|
||||
Update(0.0f, GameMain.Server.EndGame);
|
||||
messages.ForEach(traitor => traitor.Value.ForEach(message => traitor.Key.SendChatMessageBox(message)));
|
||||
Update(0.0f, () => { GameMain.Server.TraitorManager.ShouldEndRound = true; });
|
||||
#if SERVER
|
||||
foreach (var traitor in Traitors.Values)
|
||||
{
|
||||
GameServer.Log(string.Format("{0} is the traitor and the current goals are:\n{1}", traitor.Character.Name, traitor.CurrentObjective?.GoalInfos != null ? TextManager.GetServerMessage(traitor.CurrentObjective?.GoalInfos) : "(empty)"), ServerLog.MessageType.ServerMessage);
|
||||
GameServer.Log($"{traitor.Character.Name} is a traitor and the current goals are:\n{(traitor.CurrentObjective?.GoalInfos != null ? TextManager.GetServerMessage(traitor.CurrentObjective?.GoalInfos) : "(empty)")}", ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
@@ -212,23 +256,41 @@ namespace Barotrauma
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Traitors.Values.Any(traitor => traitor.Character?.IsDead ?? true))
|
||||
{
|
||||
Traitors.Values.ForEach(traitor => traitor.UpdateCurrentObjective("", Identifier));
|
||||
return;
|
||||
}
|
||||
var startedObjectives = new List<Objective>();
|
||||
foreach (var traitor in Traitors.Values)
|
||||
{
|
||||
if (traitor.Character.IsDead)
|
||||
startedObjectives.Clear();
|
||||
while (pendingObjectives.Count > 0)
|
||||
{
|
||||
traitor.UpdateCurrentObjective("");
|
||||
}
|
||||
}
|
||||
int previousCompletedCount = completedObjectives.Count;
|
||||
int startedCount = 0;
|
||||
while (pendingObjectives.Count > 0)
|
||||
{
|
||||
var objective = pendingObjectives[0];
|
||||
if (!objective.IsStarted)
|
||||
{
|
||||
if (!objective.Start(Traitors["traitor"]))
|
||||
var objective = GetCurrentObjective(traitor);
|
||||
if (objective == null)
|
||||
{
|
||||
pendingObjectives.RemoveAt(0);
|
||||
// No more objectives left for traitor or waiting for another traitor's objective.
|
||||
break;
|
||||
}
|
||||
if (!objective.IsStarted)
|
||||
{
|
||||
if (!objective.Start(traitor))
|
||||
{
|
||||
//the mission fails if an objective cannot be started
|
||||
if (completedObjectives.Count > 0)
|
||||
{
|
||||
objective.EndMessage();
|
||||
}
|
||||
pendingObjectives.Clear();
|
||||
break;
|
||||
}
|
||||
startedObjectives.Add(objective);
|
||||
}
|
||||
objective.Update(deltaTime);
|
||||
if (objective.IsCompleted)
|
||||
{
|
||||
pendingObjectives.Remove(objective);
|
||||
completedObjectives.Add(objective);
|
||||
if (pendingObjectives.Count > 0)
|
||||
{
|
||||
@@ -236,41 +298,19 @@ namespace Barotrauma
|
||||
}
|
||||
continue;
|
||||
}
|
||||
++startedCount;
|
||||
}
|
||||
objective.Update(deltaTime);
|
||||
if (objective.IsCompleted)
|
||||
{
|
||||
pendingObjectives.RemoveAt(0);
|
||||
completedObjectives.Add(objective);
|
||||
if (pendingObjectives.Count > 0)
|
||||
if (objective.IsStarted && !objective.CanBeCompleted)
|
||||
{
|
||||
objective.EndMessage();
|
||||
pendingObjectives.Clear();
|
||||
}
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (!objective.CanBeCompleted)
|
||||
if (pendingObjectives.Count > 0)
|
||||
{
|
||||
objective.EndMessage();
|
||||
objective.End(true);
|
||||
pendingObjectives.Clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
int completedMax = completedObjectives.Count - 1;
|
||||
for (int i = previousCompletedCount; i <= completedMax; ++i)
|
||||
{
|
||||
var objective = completedObjectives[i];
|
||||
objective.End(i < completedMax || pendingObjectives.Count > 0);
|
||||
}
|
||||
if (pendingObjectives.Count > 0)
|
||||
{
|
||||
if (startedCount > 0)
|
||||
{
|
||||
pendingObjectives[0].StartMessage();
|
||||
startedObjectives.ForEach(objective => objective.StartMessage());
|
||||
}
|
||||
}
|
||||
else if (completedObjectives.Count >= allObjectives.Count)
|
||||
if (completedObjectives.Count >= allObjectives.Count)
|
||||
{
|
||||
foreach (var traitor in Traitors)
|
||||
{
|
||||
@@ -303,8 +343,9 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public TraitorMission(string startText, string globalEndMessageSuccessTextId, string globalEndMessageSuccessDeadTextId, string globalEndMessageSuccessDetainedTextId, string globalEndMessageFailureTextId, string globalEndMessageFailureDeadTextId, string globalEndMessageFailureDetainedTextId, params Objective[] objectives)
|
||||
public TraitorMission(string identifier, string startText, string globalEndMessageSuccessTextId, string globalEndMessageSuccessDeadTextId, string globalEndMessageSuccessDetainedTextId, string globalEndMessageFailureTextId, string globalEndMessageFailureDeadTextId, string globalEndMessageFailureDetainedTextId, IEnumerable<KeyValuePair<string, RoleFilter>> roles, ICollection<Objective> objectives)
|
||||
{
|
||||
Identifier = identifier;
|
||||
StartText = startText;
|
||||
GlobalEndMessageSuccessTextId = globalEndMessageSuccessTextId;
|
||||
GlobalEndMessageSuccessDeadTextId = globalEndMessageSuccessDeadTextId;
|
||||
@@ -312,6 +353,10 @@ namespace Barotrauma
|
||||
GlobalEndMessageFailureTextId = globalEndMessageFailureTextId;
|
||||
GlobalEndMessageFailureDeadTextId = globalEndMessageFailureDeadTextId;
|
||||
GlobalEndMessageFailureDetainedTextId = globalEndMessageFailureDetainedTextId;
|
||||
foreach (var role in roles)
|
||||
{
|
||||
Roles.Add(role.Key, role.Value);
|
||||
}
|
||||
allObjectives.AddRange(objectives);
|
||||
pendingObjectives.AddRange(objectives);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma {
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class TraitorMissionPrefab
|
||||
{
|
||||
public class TraitorMissionEntry
|
||||
@@ -98,6 +98,7 @@ namespace Barotrauma {
|
||||
private static Dictionary<string, TargetFilter> targetFilters = new Dictionary<string, TargetFilter>()
|
||||
{
|
||||
{ "job", (value, character) => value.Equals(character.Info.Job.Prefab.Identifier, StringComparison.OrdinalIgnoreCase) },
|
||||
{ "role", (value, character) => value.Equals(GameMain.Server.TraitorManager.GetTraitorRole(character), StringComparison.OrdinalIgnoreCase) }
|
||||
};
|
||||
|
||||
public Traitor.Goal Instantiate()
|
||||
@@ -256,7 +257,16 @@ namespace Barotrauma {
|
||||
}
|
||||
}
|
||||
|
||||
public class Objective
|
||||
|
||||
public abstract class ObjectiveBase
|
||||
{
|
||||
public HashSet<string> Roles { get; } = new HashSet<string>();
|
||||
|
||||
public abstract void InstantiateGoals();
|
||||
public abstract Traitor.Objective Instantiate(IEnumerable<string> roles);
|
||||
}
|
||||
|
||||
protected class Objective : ObjectiveBase
|
||||
{
|
||||
public string InfoText { get; internal set; }
|
||||
public string StartMessageTextId { get; internal set; }
|
||||
@@ -271,16 +281,24 @@ namespace Barotrauma {
|
||||
|
||||
public readonly List<Goal> Goals = new List<Goal>();
|
||||
|
||||
public Traitor.Objective Instantiate()
|
||||
private List<Traitor.Goal> goalInstances = null;
|
||||
|
||||
public override void InstantiateGoals()
|
||||
{
|
||||
var result = new Traitor.Objective(InfoText, ShuffleGoalsCount, Goals.ConvertAll(goal => {
|
||||
goalInstances = Goals.ConvertAll(goal =>
|
||||
{
|
||||
var instance = goal.Instantiate();
|
||||
if (instance == null)
|
||||
{
|
||||
GameServer.Log($"Failed to instantiate goal \"{goal.Type}\".", ServerLog.MessageType.Error);
|
||||
}
|
||||
return instance;
|
||||
}).FindAll(goal => goal != null).ToArray());
|
||||
}).FindAll(goal => goal != null);
|
||||
}
|
||||
|
||||
public override Traitor.Objective Instantiate(IEnumerable<string> roles)
|
||||
{
|
||||
var result = new Traitor.Objective(InfoText, ShuffleGoalsCount, roles.ToArray(), goalInstances);
|
||||
if (StartMessageTextId != null)
|
||||
{
|
||||
result.StartMessageTextId = StartMessageTextId;
|
||||
@@ -316,14 +334,43 @@ namespace Barotrauma {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/*
|
||||
public class Role
|
||||
|
||||
protected class WaitObjective : ObjectiveBase
|
||||
{
|
||||
public string Job;
|
||||
private Traitor.GoalWaitForTraitors sharedGoal;
|
||||
|
||||
public override void InstantiateGoals()
|
||||
{
|
||||
sharedGoal = new Traitor.GoalWaitForTraitors(Roles.Count);
|
||||
}
|
||||
|
||||
public override Traitor.Objective Instantiate(IEnumerable<string> roles)
|
||||
{
|
||||
return new Traitor.Objective("TraitorObjectiveInfoTextWaitForOtherTraitors", -1, roles.ToArray(), new[] { sharedGoal });
|
||||
}
|
||||
|
||||
public WaitObjective(ICollection<string> roles)
|
||||
{
|
||||
Roles.UnionWith(roles);
|
||||
}
|
||||
}
|
||||
|
||||
public class Role
|
||||
{
|
||||
public readonly Traitor.TraitorMission.RoleFilter Filter;
|
||||
|
||||
public Role(IEnumerable<Traitor.TraitorMission.RoleFilter> filters)
|
||||
{
|
||||
Filter = character => filters.All(filter => filter(character));
|
||||
}
|
||||
|
||||
public Role()
|
||||
{
|
||||
Filter = character => true;
|
||||
}
|
||||
}
|
||||
public readonly Dictionary<string, Role> Roles = new Dictionary<string, Role>();
|
||||
*/
|
||||
|
||||
public readonly string Identifier;
|
||||
public readonly string StartText;
|
||||
public readonly string EndMessageSuccessText;
|
||||
@@ -333,11 +380,43 @@ namespace Barotrauma {
|
||||
public readonly string EndMessageFailureDeadText;
|
||||
public readonly string EndMessageFailureDetainedText;
|
||||
|
||||
public readonly List<Objective> Objectives = new List<Objective>();
|
||||
public readonly List<ObjectiveBase> Objectives = new List<ObjectiveBase>();
|
||||
|
||||
public Traitor.TraitorMission Instantiate()
|
||||
{
|
||||
var objectivesWithSync = new List<ObjectiveBase>();
|
||||
var objectivesCount = Objectives.Count;
|
||||
if (objectivesCount > 0)
|
||||
{
|
||||
var pendingRoles = new HashSet<string>();
|
||||
var pendingCount = 1;
|
||||
objectivesWithSync.Add(Objectives[0]);
|
||||
pendingRoles.UnionWith(Objectives[0].Roles);
|
||||
for (var i = 1; i < objectivesCount; ++i)
|
||||
{
|
||||
var objective = Objectives[i];
|
||||
if (pendingRoles.IsSupersetOf(objective.Roles))
|
||||
{
|
||||
if (pendingCount > 1)
|
||||
{
|
||||
objectivesWithSync.Add(new WaitObjective(objective.Roles));
|
||||
}
|
||||
pendingRoles.Clear();
|
||||
pendingCount = 0;
|
||||
}
|
||||
objectivesWithSync.Add(objective);
|
||||
pendingRoles.UnionWith(objective.Roles);
|
||||
++pendingCount;
|
||||
}
|
||||
if (pendingCount > 1 && pendingRoles.IsSubsetOf(Roles.Keys))
|
||||
{
|
||||
// TODO: If last objective includes only one traitor, other traitors will get the wrong end message.
|
||||
objectivesWithSync.Add(new WaitObjective(Roles.Keys));
|
||||
}
|
||||
}
|
||||
|
||||
return new Traitor.TraitorMission(
|
||||
Identifier,
|
||||
StartText ?? "TraitorMissionStartMessage",
|
||||
EndMessageSuccessText ?? "TraitorObjectiveEndMessageSuccess",
|
||||
EndMessageSuccessDeadText ?? "TraitorObjectiveEndMessageSuccessDead",
|
||||
@@ -345,7 +424,12 @@ namespace Barotrauma {
|
||||
EndMessageFailureText ?? "TraitorObjectiveEndMessageFailure",
|
||||
EndMessageFailureDeadText ?? "TraitorObjectiveEndMessageFailureDead",
|
||||
EndMessageFailureDetainedText ?? "TraitorObjectiveEndMessageFailureDetained",
|
||||
Objectives.ConvertAll(objective => objective.Instantiate()).ToArray());
|
||||
Roles.ToDictionary(kv => kv.Key, kv => kv.Value.Filter),
|
||||
objectivesWithSync.SelectMany(objective =>
|
||||
{
|
||||
objective.InstantiateGoals();
|
||||
return objective.Roles.Select(role => objective.Instantiate(new[] { role }));
|
||||
}).ToArray());
|
||||
}
|
||||
|
||||
protected Goal LoadGoal(XElement goalRoot)
|
||||
@@ -354,10 +438,22 @@ namespace Barotrauma {
|
||||
return new Goal(goalType, goalRoot);
|
||||
}
|
||||
|
||||
protected Objective LoadObjective(XElement objectiveRoot)
|
||||
{
|
||||
var result = new Objective();
|
||||
result.ShuffleGoalsCount = objectiveRoot.GetAttributeInt("shuffleGoalsCount", -1);
|
||||
protected Objective LoadObjective(XElement objectiveRoot, string[] allRoles)
|
||||
{
|
||||
var allRolesSet = new HashSet<string>(allRoles);
|
||||
var result = new Objective
|
||||
{
|
||||
ShuffleGoalsCount = objectiveRoot.GetAttributeInt("shuffleGoalsCount", -1)
|
||||
};
|
||||
var objectiveRoles = objectiveRoot.GetAttributeStringArray("roles", allRoles);
|
||||
if (!allRolesSet.IsSupersetOf(objectiveRoles))
|
||||
{
|
||||
var unrecognized = new HashSet<string>(objectiveRoles);
|
||||
unrecognized.ExceptWith(allRoles);
|
||||
GameServer.Log($"Undefined role(s) \"{string.Join(", ", unrecognized)}\" set for Objective.", ServerLog.MessageType.Error);
|
||||
}
|
||||
result.Roles.UnionWith(allRolesSet.Intersect(objectiveRoles));
|
||||
|
||||
foreach (var element in objectiveRoot.Elements())
|
||||
{
|
||||
using (var checker = new AttributeChecker(element))
|
||||
@@ -410,7 +506,7 @@ namespace Barotrauma {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GameServer.Log($"Unrecognized element \"{element.Name}\"under Objective.", ServerLog.MessageType.Error);
|
||||
GameServer.Log($"Unrecognized element \"{element.Name}\" under Objective.", ServerLog.MessageType.Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -418,6 +514,18 @@ namespace Barotrauma {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Role LoadRole(XElement roleRoot)
|
||||
{
|
||||
var filters = new List<Traitor.TraitorMission.RoleFilter>();
|
||||
var jobs = roleRoot.GetAttributeStringArray("jobs", null);
|
||||
if (jobs != null)
|
||||
{
|
||||
var jobsSet = new HashSet<string>(jobs.Select(job => job.ToLower(CultureInfo.InvariantCulture)));
|
||||
filters.Add(character => character.Info?.Job != null && jobsSet.Contains(character.Info.Job.Name.ToLower(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
return new Role(filters);
|
||||
}
|
||||
|
||||
public TraitorMissionPrefab(XElement missionRoot)
|
||||
{
|
||||
Identifier = missionRoot.GetAttributeString("identifier", null);
|
||||
@@ -427,6 +535,27 @@ namespace Barotrauma {
|
||||
{
|
||||
switch (element.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "role":
|
||||
checker.Required("id");
|
||||
checker.Optional("jobs");
|
||||
Roles.Add(element.GetAttributeString("id", null), LoadRole(element));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Roles.Any())
|
||||
{
|
||||
Roles.Add("traitor", new Role());
|
||||
}
|
||||
foreach (var element in missionRoot.Elements())
|
||||
{
|
||||
using (var checker = new AttributeChecker(element))
|
||||
{
|
||||
switch (element.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "role":
|
||||
// handled above
|
||||
break;
|
||||
case "startinfotext":
|
||||
checker.Required("id");
|
||||
StartText = element.GetAttributeString("id", null);
|
||||
@@ -457,7 +586,7 @@ namespace Barotrauma {
|
||||
break;
|
||||
case "objective":
|
||||
{
|
||||
var objective = LoadObjective(element);
|
||||
var objective = LoadObjective(element, Roles.Keys.ToArray());
|
||||
if (objective != null)
|
||||
{
|
||||
Objectives.Add(objective);
|
||||
|
||||
Reference in New Issue
Block a user