mirror of
https://github.com/AsgardEternal/SquadJS.git
synced 2024-09-28 23:24:26 -05:00
Merge branch 'Thomas-Smyth-master' into master
This commit is contained in:
commit
a67f90ba7e
556
README.md
556
README.md
@ -266,107 +266,61 @@ The following is a list of plugins built into SquadJS, you can click their title
|
|||||||
Interested in creating your own plugin? [See more here](./squad-server/plugins/readme.md)
|
Interested in creating your own plugin? [See more here](./squad-server/plugins/readme.md)
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>AutoKickUnassigned</summary>
|
<summary>DiscordSubsystemRestarter</summary>
|
||||||
<h2>AutoKickUnassigned</h2>
|
<h2>DiscordSubsystemRestarter</h2>
|
||||||
<p>The <code>AutoKickUnassigned</code> plugin will automatically kick players that are not in a squad after a specified ammount of time.</p>
|
<p>The <code>DiscordSubSystemRestarter</code> plugin allows you to manually restart SquadJS subsystems in case an issues arises with them.<ul><li><code>!squadjs restartsubsystem rcon</code></li><li><code>!squadjs restartsubsystem logparser</code></li></ul></p>
|
||||||
<h3>Options</h3>
|
<h3>Options</h3>
|
||||||
<ul><li><h4>warningMessage</h4>
|
<ul><li><h4>discordClient (Required)</h4>
|
||||||
<h6>Description</h6>
|
<h6>Description</h6>
|
||||||
<p>Message SquadJS will send to players warning them they will be kicked</p>
|
<p>Discord connector name.</p>
|
||||||
<h6>Default</h6>
|
<h6>Default</h6>
|
||||||
<pre><code>Join a squad, you are are unassigned and will be kicked</code></pre></li>
|
<pre><code>discord</code></pre></li>
|
||||||
<li><h4>kickMessage</h4>
|
<li><h4>role (Required)</h4>
|
||||||
<h6>Description</h6>
|
<h6>Description</h6>
|
||||||
<p>Message to send to players when they are kicked</p>
|
<p>ID of role required to run the sub system restart commands.</p>
|
||||||
<h6>Default</h6>
|
<h6>Default</h6>
|
||||||
<pre><code>Unassigned - automatically removed</code></pre></li>
|
<pre><code></code></pre></li><h6>Example</h6>
|
||||||
<li><h4>frequencyOfWarnings</h4>
|
<pre><code>667741905228136459</code></pre></ul>
|
||||||
<h6>Description</h6>
|
|
||||||
<p>How often in <b>Seconds</b> should we warn the player about being unassigned?</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>30</code></pre></li>
|
|
||||||
<li><h4>unassignedTimer</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>How long in <b>Seconds</b> to wait before a unassigned player is kicked</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>360</code></pre></li>
|
|
||||||
<li><h4>playerThreshold</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Player count required for AutoKick to start kicking players, set to -1 to disable</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>93</code></pre></li>
|
|
||||||
<li><h4>roundStartDelay</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Time delay in <b>Seconds</b> from start of the round before AutoKick starts kicking again</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>900</code></pre></li>
|
|
||||||
<li><h4>ignoreAdmins</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p><ul><li><code>true</code>: Admins will <b>NOT</b> be kicked</li><li><code>false</code>: Admins <b>WILL</b> be kicked</li></ul></p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>false</code></pre></li>
|
|
||||||
<li><h4>ignoreWhitelist</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p><ul><li><code>true</code>: Reserve slot players will <b>NOT</b> be kicked</li><li><code>false</code>: Reserve slot players <b>WILL</b> be kicked</li></ul></p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>false</code></pre></li></ul>
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>AutoTKWarn</summary>
|
<summary>DiscordChat</summary>
|
||||||
<h2>AutoTKWarn</h2>
|
<h2>DiscordChat</h2>
|
||||||
<p>The <code>AutoTkWarn</code> plugin will automatically warn players with a message when they teamkill.</p>
|
<p>The <code>DiscordChat</code> plugin will log in-game chat to a Discord channel.</p>
|
||||||
<h3>Options</h3>
|
<h3>Options</h3>
|
||||||
<ul><li><h4>message</h4>
|
<ul><li><h4>discordClient (Required)</h4>
|
||||||
<h6>Description</h6>
|
<h6>Description</h6>
|
||||||
<p>The message to warn players with.</p>
|
<p>Discord connector name.</p>
|
||||||
<h6>Default</h6>
|
<h6>Default</h6>
|
||||||
<pre><code>Please apologise for ALL TKs in ALL chat!</code></pre></li></ul>
|
<pre><code>discord</code></pre></li>
|
||||||
</details>
|
<li><h4>channelID (Required)</h4>
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>ChatCommands</summary>
|
|
||||||
<h2>ChatCommands</h2>
|
|
||||||
<p>The <code>ChatCommands</code> plugin can be configured to make chat commands that broadcast or warn the caller with present messages.</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>commands</h4>
|
|
||||||
<h6>Description</h6>
|
<h6>Description</h6>
|
||||||
<p>An array of objects containing the following properties: <ul><li><code>command</code> - The command that initiates the message.</li><li><code>type</code> - Either <code>warn</code> or <code>broadcast</code>.</li><li><code>response</code> - The message to respond with.</li><li><code>ignoreChats</code> - A list of chats to ignore the commands in. Use this to limit it to admins.</li></ul></p>
|
<p>The ID of the channel to log admin broadcasts to.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code></code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>667741905228136459</code></pre>
|
||||||
|
<li><h4>chatColors</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The color of the embed for each chat.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>{}</code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>{
|
||||||
|
"ChatAll": 16761867
|
||||||
|
}</code></pre>
|
||||||
|
<li><h4>color</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The color of the embed.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>16761867</code></pre></li>
|
||||||
|
<li><h4>ignoreChats</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>A list of chat names to ignore.</p>
|
||||||
<h6>Default</h6>
|
<h6>Default</h6>
|
||||||
<pre><code>[
|
<pre><code>[
|
||||||
{
|
"ChatSquad"
|
||||||
"command": "squadjs",
|
|
||||||
"type": "warn",
|
|
||||||
"response": "This server is powered by SquadJS.",
|
|
||||||
"ignoreChats": []
|
|
||||||
}
|
|
||||||
]</code></pre></li></ul>
|
]</code></pre></li></ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>DBLog</summary>
|
|
||||||
<h2>DBLog</h2>
|
|
||||||
<p>The <code>mysql-log</code> plugin will log various server statistics and events to a database. This is great for server performance monitoring and/or player stat tracking.
|
|
||||||
|
|
||||||
Grafana (NOT YET WORKING WITH V2):
|
|
||||||
<ul><li> <a href="https://grafana.com/">Grafana</a> is a cool way of viewing server statistics stored in the database.</li>
|
|
||||||
<li>Install Grafana.</li>
|
|
||||||
<li>Add your database as a datasource named <code>SquadJS</code>.</li>
|
|
||||||
<li>Import the <a href="https://github.com/Thomas-Smyth/SquadJS/blob/master/plugins/mysql-log/SquadJS-Dashboard.json">SquadJS Dashboard</a> to get a preconfigured MySQL only Grafana dashboard.</li>
|
|
||||||
<li>Install any missing Grafana plugins.</li></ul></p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>database (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The Sequelize connector to log server information to.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>mysql</code></pre></li>
|
|
||||||
<li><h4>overrideServerID</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>A overridden server ID.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>null</code></pre></li></ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>DiscordAdminBroadcast</summary>
|
<summary>DiscordAdminBroadcast</summary>
|
||||||
<h2>DiscordAdminBroadcast</h2>
|
<h2>DiscordAdminBroadcast</h2>
|
||||||
@ -390,29 +344,6 @@ Grafana (NOT YET WORKING WITH V2):
|
|||||||
<pre><code>16761867</code></pre></li></ul>
|
<pre><code>16761867</code></pre></li></ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>DiscordAdminCamLogs</summary>
|
|
||||||
<h2>DiscordAdminCamLogs</h2>
|
|
||||||
<p>The <code>DiscordAdminCamLogs</code> plugin will log in game admin camera usage to a Discord channel.</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>discordClient (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Discord connector name.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>discord</code></pre></li>
|
|
||||||
<li><h4>channelID (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The ID of the channel to log admin camera usage to.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code></code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>667741905228136459</code></pre>
|
|
||||||
<li><h4>color</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The color of the embed.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>16761867</code></pre></li></ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>DiscordAdminRequest</summary>
|
<summary>DiscordAdminRequest</summary>
|
||||||
<h2>DiscordAdminRequest</h2>
|
<h2>DiscordAdminRequest</h2>
|
||||||
@ -470,128 +401,6 @@ Grafana (NOT YET WORKING WITH V2):
|
|||||||
<pre><code>16761867</code></pre></li></ul>
|
<pre><code>16761867</code></pre></li></ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>DiscordChat</summary>
|
|
||||||
<h2>DiscordChat</h2>
|
|
||||||
<p>The <code>DiscordChat</code> plugin will log in-game chat to a Discord channel.</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>discordClient (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Discord connector name.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>discord</code></pre></li>
|
|
||||||
<li><h4>channelID (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The ID of the channel to log admin broadcasts to.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code></code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>667741905228136459</code></pre>
|
|
||||||
<li><h4>chatColors</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The color of the embed for each chat.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>{}</code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>{
|
|
||||||
"ChatAll": 16761867
|
|
||||||
}</code></pre>
|
|
||||||
<li><h4>color</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The color of the embed.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>16761867</code></pre></li>
|
|
||||||
<li><h4>ignoreChats</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>A list of chat names to ignore.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>[
|
|
||||||
"ChatSquad"
|
|
||||||
]</code></pre></li></ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>DiscordDebug</summary>
|
|
||||||
<h2>DiscordDebug</h2>
|
|
||||||
<p>The <code>DiscordDebug</code> plugin can be used to help debug SquadJS by dumping SquadJS events to a Discord channel.</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>discordClient (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Discord connector name.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>discord</code></pre></li>
|
|
||||||
<li><h4>channelID (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The ID of the channel to log events to.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code></code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>667741905228136459</code></pre>
|
|
||||||
<li><h4>events (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>A list of events to dump.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>[]</code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>[
|
|
||||||
"PLAYER_DIED"
|
|
||||||
]</code></pre></ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>DiscordRcon</summary>
|
|
||||||
<h2>DiscordRcon</h2>
|
|
||||||
<p>The <code>DiscordRcon</code> plugin allows a specified Discord channel to be used as a RCON console to run RCON commands.</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>discordClient (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Discord connector name.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>discord</code></pre></li>
|
|
||||||
<li><h4>channelID (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>ID of channel to turn into RCON console.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code></code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>667741905228136459</code></pre>
|
|
||||||
<li><h4>permissions</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p><ul><li>Dictionary of roles and a list of the permissions they are allowed to use.<li>If dictionary is empty (<code>{}</code>) permissions will be disabled</li><li>A list of available RCON commands can be found here <a>https://squad.gamepedia.com/Server_Administration#Admin_Console_Commands</a>.</ul></p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>{}</code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>{
|
|
||||||
"123456789123456789": [
|
|
||||||
"AdminBroadcast",
|
|
||||||
"AdminForceTeamChange",
|
|
||||||
"AdminDemoteCommander"
|
|
||||||
]
|
|
||||||
}</code></pre>
|
|
||||||
<li><h4>prependAdminNameInBroadcast</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Prepend admin names when making announcements.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>false</code></pre></li></ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>DiscordRoundWinner</summary>
|
|
||||||
<h2>DiscordRoundWinner</h2>
|
|
||||||
<p>The <code>DiscordRoundWinner</code> plugin will send the round winner to a Discord channel.</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>discordClient (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Discord connector name.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>discord</code></pre></li>
|
|
||||||
<li><h4>channelID (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The ID of the channel to log admin broadcasts to.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code></code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>667741905228136459</code></pre>
|
|
||||||
<li><h4>color</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The color of the embed.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>16761867</code></pre></li></ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>DiscordServerStatus</summary>
|
<summary>DiscordServerStatus</summary>
|
||||||
<h2>DiscordServerStatus</h2>
|
<h2>DiscordServerStatus</h2>
|
||||||
@ -626,21 +435,96 @@ Grafana (NOT YET WORKING WITH V2):
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>DiscordSubsystemRestarter</summary>
|
<summary>AutoKickUnassigned</summary>
|
||||||
<h2>DiscordSubsystemRestarter</h2>
|
<h2>AutoKickUnassigned</h2>
|
||||||
<p>The <code>DiscordSubSystemRestarter</code> plugin allows you to manually restart SquadJS subsystems in case an issues arises with them.<ul><li><code>!squadjs restartsubsystem rcon</code></li><li><code>!squadjs restartsubsystem logparser</code></li></ul></p>
|
<p>The <code>AutoKickUnassigned</code> plugin will automatically kick players that are not in a squad after a specified ammount of time.</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>warningMessage</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Message SquadJS will send to players warning them they will be kicked</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>Join a squad, you are are unassigned and will be kicked</code></pre></li>
|
||||||
|
<li><h4>kickMessage</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Message to send to players when they are kicked</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>Unassigned - automatically removed</code></pre></li>
|
||||||
|
<li><h4>frequencyOfWarnings</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>How often in <b>Seconds</b> should we warn the player about being unassigned?</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>30</code></pre></li>
|
||||||
|
<li><h4>unassignedTimer</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>How long in <b>Seconds</b> to wait before a unassigned player is kicked</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>360</code></pre></li>
|
||||||
|
<li><h4>playerThreshold</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Player count required for AutoKick to start kicking players, set to -1 to disable</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>93</code></pre></li>
|
||||||
|
<li><h4>roundStartDelay</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Time delay in <b>Seconds</b> from start of the round before AutoKick starts kicking again</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>900</code></pre></li>
|
||||||
|
<li><h4>ignoreAdmins</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p><ul><li><code>true</code>: Admins will <b>NOT</b> be kicked</li><li><code>false</code>: Admins <b>WILL</b> be kicked</li></ul></p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>false</code></pre></li>
|
||||||
|
<li><h4>ignoreWhitelist</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p><ul><li><code>true</code>: Reserve slot players will <b>NOT</b> be kicked</li><li><code>false</code>: Reserve slot players <b>WILL</b> be kicked</li></ul></p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>false</code></pre></li></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>DiscordDebug</summary>
|
||||||
|
<h2>DiscordDebug</h2>
|
||||||
|
<p>The <code>DiscordDebug</code> plugin can be used to help debug SquadJS by dumping SquadJS events to a Discord channel.</p>
|
||||||
<h3>Options</h3>
|
<h3>Options</h3>
|
||||||
<ul><li><h4>discordClient (Required)</h4>
|
<ul><li><h4>discordClient (Required)</h4>
|
||||||
<h6>Description</h6>
|
<h6>Description</h6>
|
||||||
<p>Discord connector name.</p>
|
<p>Discord connector name.</p>
|
||||||
<h6>Default</h6>
|
<h6>Default</h6>
|
||||||
<pre><code>discord</code></pre></li>
|
<pre><code>discord</code></pre></li>
|
||||||
<li><h4>role (Required)</h4>
|
<li><h4>channelID (Required)</h4>
|
||||||
<h6>Description</h6>
|
<h6>Description</h6>
|
||||||
<p>ID of role required to run the sub system restart commands.</p>
|
<p>The ID of the channel to log events to.</p>
|
||||||
<h6>Default</h6>
|
<h6>Default</h6>
|
||||||
<pre><code></code></pre></li><h6>Example</h6>
|
<pre><code></code></pre></li><h6>Example</h6>
|
||||||
<pre><code>667741905228136459</code></pre></ul>
|
<pre><code>667741905228136459</code></pre>
|
||||||
|
<li><h4>events (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>A list of events to dump.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>[]</code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>[
|
||||||
|
"PLAYER_DIED"
|
||||||
|
]</code></pre></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>IntervalledBroadcasts</summary>
|
||||||
|
<h2>IntervalledBroadcasts</h2>
|
||||||
|
<p>The <code>IntervalledBroadcasts</code> plugin allows you to set broadcasts, which will be broadcasted at preset intervals</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>broadcasts</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Messages to broadcast.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>[]</code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>[
|
||||||
|
"This server is powered by SquadJS."
|
||||||
|
]</code></pre>
|
||||||
|
<li><h4>interval</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Frequency of the broadcasts in milliseconds.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>300000</code></pre></li></ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -671,49 +555,6 @@ Grafana (NOT YET WORKING WITH V2):
|
|||||||
<pre><code>false</code></pre></li></ul>
|
<pre><code>false</code></pre></li></ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>IntervalledBroadcasts</summary>
|
|
||||||
<h2>IntervalledBroadcasts</h2>
|
|
||||||
<p>The <code>IntervalledBroadcasts</code> plugin allows you to set broadcasts, which will be broadcasted at preset intervals</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>broadcasts</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Messages to broadcast.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>[]</code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>[
|
|
||||||
"This server is powered by SquadJS."
|
|
||||||
]</code></pre>
|
|
||||||
<li><h4>interval</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Frequency of the broadcasts in milliseconds.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>300000</code></pre></li></ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>SCBLInfo</summary>
|
|
||||||
<h2>SCBLInfo</h2>
|
|
||||||
<p>The <code>SCBLInfo</code> plugin alerts admins when a harmful player is detected joining their server based on data from the <a href="https://squad-community-ban-list.com/">Squad Community Ban List</a>.</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
<ul><li><h4>discordClient (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Discord connector name.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>discord</code></pre></li>
|
|
||||||
<li><h4>channelID (Required)</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>The ID of the channel to alert admins through.</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code></code></pre></li><h6>Example</h6>
|
|
||||||
<pre><code>667741905228136459</code></pre>
|
|
||||||
<li><h4>threshold</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>Admins will be alerted when a player has this or more reputation points. For more information on reputation points, see the <a href="https://squad-community-ban-list.com/faq">Squad Community Ban List's FAQ</a></p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>6</code></pre></li></ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>SeedingMode</summary>
|
<summary>SeedingMode</summary>
|
||||||
<h2>SeedingMode</h2>
|
<h2>SeedingMode</h2>
|
||||||
@ -751,6 +592,142 @@ Grafana (NOT YET WORKING WITH V2):
|
|||||||
<pre><code>Live!</code></pre></li></ul>
|
<pre><code>Live!</code></pre></li></ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>AutoTKWarn</summary>
|
||||||
|
<h2>AutoTKWarn</h2>
|
||||||
|
<p>The <code>AutoTkWarn</code> plugin will automatically warn players with a message when they teamkill.</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>message</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The message to warn players with.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>Please apologise for ALL TKs in ALL chat!</code></pre></li></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>DBLog</summary>
|
||||||
|
<h2>DBLog</h2>
|
||||||
|
<p>The <code>mysql-log</code> plugin will log various server statistics and events to a database. This is great for server performance monitoring and/or player stat tracking.
|
||||||
|
|
||||||
|
Grafana (NOT YET WORKING WITH V2):
|
||||||
|
<ul><li> <a href="https://grafana.com/">Grafana</a> is a cool way of viewing server statistics stored in the database.</li>
|
||||||
|
<li>Install Grafana.</li>
|
||||||
|
<li>Add your database as a datasource named <code>SquadJS</code>.</li>
|
||||||
|
<li>Import the <a href="https://github.com/Thomas-Smyth/SquadJS/blob/master/plugins/mysql-log/SquadJS-Dashboard.json">SquadJS Dashboard</a> to get a preconfigured MySQL only Grafana dashboard.</li>
|
||||||
|
<li>Install any missing Grafana plugins.</li></ul></p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>database (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The Sequelize connector to log server information to.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>mysql</code></pre></li>
|
||||||
|
<li><h4>overrideServerID</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>A overridden server ID.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>null</code></pre></li></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>DiscordRoundWinner</summary>
|
||||||
|
<h2>DiscordRoundWinner</h2>
|
||||||
|
<p>The <code>DiscordRoundWinner</code> plugin will send the round winner to a Discord channel.</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>discordClient (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Discord connector name.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>discord</code></pre></li>
|
||||||
|
<li><h4>channelID (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The ID of the channel to log admin broadcasts to.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code></code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>667741905228136459</code></pre>
|
||||||
|
<li><h4>color</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The color of the embed.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>16761867</code></pre></li></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>ChatCommands</summary>
|
||||||
|
<h2>ChatCommands</h2>
|
||||||
|
<p>The <code>ChatCommands</code> plugin can be configured to make chat commands that broadcast or warn the caller with present messages.</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>commands</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>An array of objects containing the following properties: <ul><li><code>command</code> - The command that initiates the message.</li><li><code>type</code> - Either <code>warn</code> or <code>broadcast</code>.</li><li><code>response</code> - The message to respond with.</li><li><code>ignoreChats</code> - A list of chats to ignore the commands in. Use this to limit it to admins.</li></ul></p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>[
|
||||||
|
{
|
||||||
|
"command": "squadjs",
|
||||||
|
"type": "warn",
|
||||||
|
"response": "This server is powered by SquadJS.",
|
||||||
|
"ignoreChats": []
|
||||||
|
}
|
||||||
|
]</code></pre></li></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>DiscordAdminCamLogs</summary>
|
||||||
|
<h2>DiscordAdminCamLogs</h2>
|
||||||
|
<p>The <code>DiscordAdminCamLogs</code> plugin will log in game admin camera usage to a Discord channel.</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>discordClient (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Discord connector name.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>discord</code></pre></li>
|
||||||
|
<li><h4>channelID (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The ID of the channel to log admin camera usage to.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code></code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>667741905228136459</code></pre>
|
||||||
|
<li><h4>color</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The color of the embed.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>16761867</code></pre></li></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>DiscordRcon</summary>
|
||||||
|
<h2>DiscordRcon</h2>
|
||||||
|
<p>The <code>DiscordRcon</code> plugin allows a specified Discord channel to be used as a RCON console to run RCON commands.</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>discordClient (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Discord connector name.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>discord</code></pre></li>
|
||||||
|
<li><h4>channelID (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>ID of channel to turn into RCON console.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code></code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>667741905228136459</code></pre>
|
||||||
|
<li><h4>permissions</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p><ul><li>Dictionary of roles and a list of the permissions they are allowed to use.<li>If dictionary is empty (<code>{}</code>) permissions will be disabled</li><li>A list of available RCON commands can be found here <a>https://squad.gamepedia.com/Server_Administration#Admin_Console_Commands</a>.</ul></p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>{}</code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>{
|
||||||
|
"123456789123456789": [
|
||||||
|
"AdminBroadcast",
|
||||||
|
"AdminForceTeamChange",
|
||||||
|
"AdminDemoteCommander"
|
||||||
|
]
|
||||||
|
}</code></pre>
|
||||||
|
<li><h4>prependAdminNameInBroadcast</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Prepend admin names when making announcements.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>false</code></pre></li></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>TeamRandomizer</summary>
|
<summary>TeamRandomizer</summary>
|
||||||
<h2>TeamRandomizer</h2>
|
<h2>TeamRandomizer</h2>
|
||||||
@ -763,6 +740,29 @@ Grafana (NOT YET WORKING WITH V2):
|
|||||||
<pre><code>randomize</code></pre></li></ul>
|
<pre><code>randomize</code></pre></li></ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>SCBLInfo</summary>
|
||||||
|
<h2>SCBLInfo</h2>
|
||||||
|
<p>The <code>SCBLInfo</code> plugin alerts admins when a harmful player is detected joining their server based on data from the <a href="https://squad-community-ban-list.com/">Squad Community Ban List</a>.</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
<ul><li><h4>discordClient (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Discord connector name.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>discord</code></pre></li>
|
||||||
|
<li><h4>channelID (Required)</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>The ID of the channel to alert admins through.</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code></code></pre></li><h6>Example</h6>
|
||||||
|
<pre><code>667741905228136459</code></pre>
|
||||||
|
<li><h4>threshold</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>Admins will be alerted when a player has this or more reputation points. For more information on reputation points, see the <a href="https://squad-community-ban-list.com/faq">Squad Community Ban List's FAQ</a></p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>6</code></pre></li></ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Statement on Accuracy
|
## Statement on Accuracy
|
||||||
|
203
config.json
203
config.json
@ -22,43 +22,6 @@
|
|||||||
},
|
},
|
||||||
"connectors": {
|
"connectors": {
|
||||||
"discord": "Discord Login Token",
|
"discord": "Discord Login Token",
|
||||||
"squadlayerpool": {
|
|
||||||
"type": "buildPoolFromFilter",
|
|
||||||
"filter": {
|
|
||||||
"whitelistedLayers": null,
|
|
||||||
"blacklistedLayers": null,
|
|
||||||
"whitelistedMaps": null,
|
|
||||||
"blacklistedMaps": null,
|
|
||||||
"whitelistedGamemodes": null,
|
|
||||||
"blacklistedGamemodes": [
|
|
||||||
"Training"
|
|
||||||
],
|
|
||||||
"flagCountMin": null,
|
|
||||||
"flagCountMax": null,
|
|
||||||
"hasCommander": null,
|
|
||||||
"hasTanks": null,
|
|
||||||
"hasHelicopters": null
|
|
||||||
},
|
|
||||||
"activeLayerFilter": {
|
|
||||||
"historyResetTime": 18000000,
|
|
||||||
"layerHistoryTolerance": 8,
|
|
||||||
"mapHistoryTolerance": 4,
|
|
||||||
"gamemodeHistoryTolerance": {
|
|
||||||
"Invasion": 4
|
|
||||||
},
|
|
||||||
"gamemodeRepetitiveTolerance": {
|
|
||||||
"Invasion": 4
|
|
||||||
},
|
|
||||||
"playerCountComplianceEnabled": true,
|
|
||||||
"factionComplianceEnabled": true,
|
|
||||||
"factionHistoryTolerance": {
|
|
||||||
"RUS": 4
|
|
||||||
},
|
|
||||||
"factionRepetitiveTolerance": {
|
|
||||||
"RUS": 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"host": "host",
|
"host": "host",
|
||||||
"port": 3306,
|
"port": 3306,
|
||||||
@ -70,40 +33,22 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"plugin": "AutoKickUnassigned",
|
"plugin": "DiscordSubsystemRestarter",
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"warningMessage": "Join a squad, you are are unassigned and will be kicked",
|
"discordClient": "discord",
|
||||||
"kickMessage": "Unassigned - automatically removed",
|
"role": ""
|
||||||
"frequencyOfWarnings": 30,
|
|
||||||
"unassignedTimer": 360,
|
|
||||||
"playerThreshold": 93,
|
|
||||||
"roundStartDelay": 900,
|
|
||||||
"ignoreAdmins": false,
|
|
||||||
"ignoreWhitelist": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"plugin": "AutoTKWarn",
|
"plugin": "DiscordChat",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"message": "Please apologise for ALL TKs in ALL chat!"
|
"discordClient": "discord",
|
||||||
},
|
"channelID": "",
|
||||||
{
|
"chatColors": {},
|
||||||
"plugin": "ChatCommands",
|
"color": 16761867,
|
||||||
"enabled": true,
|
"ignoreChats": [
|
||||||
"commands": [
|
"ChatSquad"
|
||||||
{
|
|
||||||
"command": "squadjs",
|
|
||||||
"type": "warn",
|
|
||||||
"response": "This server is powered by SquadJS.",
|
|
||||||
"ignoreChats": []
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"plugin": "DBLog",
|
|
||||||
"enabled": false,
|
|
||||||
"database": "mysql",
|
|
||||||
"overrideServerID": null
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"plugin": "DiscordAdminBroadcast",
|
"plugin": "DiscordAdminBroadcast",
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
@ -111,13 +56,6 @@
|
|||||||
"channelID": "",
|
"channelID": "",
|
||||||
"color": 16761867
|
"color": 16761867
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"plugin": "DiscordAdminCamLogs",
|
|
||||||
"enabled": false,
|
|
||||||
"discordClient": "discord",
|
|
||||||
"channelID": "",
|
|
||||||
"color": 16761867
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"plugin": "DiscordAdminRequest",
|
"plugin": "DiscordAdminRequest",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@ -131,15 +69,24 @@
|
|||||||
"color": 16761867
|
"color": 16761867
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"plugin": "DiscordChat",
|
"plugin": "DiscordServerStatus",
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"discordClient": "discord",
|
"discordClient": "discord",
|
||||||
"channelID": "",
|
"messageIDs": [],
|
||||||
"chatColors": {},
|
"updateInterval": 60000,
|
||||||
"color": 16761867,
|
"disableStatus": false
|
||||||
"ignoreChats": [
|
},
|
||||||
"ChatSquad"
|
{
|
||||||
]
|
"plugin": "AutoKickUnassigned",
|
||||||
|
"enabled": true,
|
||||||
|
"warningMessage": "Join a squad, you are are unassigned and will be kicked",
|
||||||
|
"kickMessage": "Unassigned - automatically removed",
|
||||||
|
"frequencyOfWarnings": 30,
|
||||||
|
"unassignedTimer": 360,
|
||||||
|
"playerThreshold": 93,
|
||||||
|
"roundStartDelay": 900,
|
||||||
|
"ignoreAdmins": false,
|
||||||
|
"ignoreWhitelist": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"plugin": "DiscordDebug",
|
"plugin": "DiscordDebug",
|
||||||
@ -149,33 +96,10 @@
|
|||||||
"events": []
|
"events": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"plugin": "DiscordRcon",
|
"plugin": "IntervalledBroadcasts",
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"discordClient": "discord",
|
"broadcasts": [],
|
||||||
"channelID": "",
|
"interval": 300000
|
||||||
"permissions": {},
|
|
||||||
"prependAdminNameInBroadcast": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"plugin": "DiscordRoundWinner",
|
|
||||||
"enabled": true,
|
|
||||||
"discordClient": "discord",
|
|
||||||
"channelID": "",
|
|
||||||
"color": 16761867
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"plugin": "DiscordServerStatus",
|
|
||||||
"enabled": false,
|
|
||||||
"discordClient": "discord",
|
|
||||||
"messageIDs": [],
|
|
||||||
"updateInterval": 60000,
|
|
||||||
"disableStatus": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"plugin": "DiscordSubsystemRestarter",
|
|
||||||
"enabled": false,
|
|
||||||
"discordClient": "discord",
|
|
||||||
"role": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"plugin": "DiscordTeamkill",
|
"plugin": "DiscordTeamkill",
|
||||||
@ -185,19 +109,6 @@
|
|||||||
"color": 16761867,
|
"color": 16761867,
|
||||||
"disableSCBL": false
|
"disableSCBL": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"plugin": "IntervalledBroadcasts",
|
|
||||||
"enabled": false,
|
|
||||||
"broadcasts": [],
|
|
||||||
"interval": 300000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"plugin": "SCBLInfo",
|
|
||||||
"enabled": true,
|
|
||||||
"discordClient": "discord",
|
|
||||||
"channelID": "",
|
|
||||||
"threshold": 6
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"plugin": "SeedingMode",
|
"plugin": "SeedingMode",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@ -208,10 +119,62 @@
|
|||||||
"liveThreshold": 52,
|
"liveThreshold": 52,
|
||||||
"liveMessage": "Live!"
|
"liveMessage": "Live!"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"plugin": "AutoTKWarn",
|
||||||
|
"enabled": true,
|
||||||
|
"message": "Please apologise for ALL TKs in ALL chat!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"plugin": "DBLog",
|
||||||
|
"enabled": false,
|
||||||
|
"database": "mysql",
|
||||||
|
"overrideServerID": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"plugin": "DiscordRoundWinner",
|
||||||
|
"enabled": true,
|
||||||
|
"discordClient": "discord",
|
||||||
|
"channelID": "",
|
||||||
|
"color": 16761867
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"plugin": "ChatCommands",
|
||||||
|
"enabled": true,
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "squadjs",
|
||||||
|
"type": "warn",
|
||||||
|
"response": "This server is powered by SquadJS.",
|
||||||
|
"ignoreChats": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"plugin": "DiscordAdminCamLogs",
|
||||||
|
"enabled": false,
|
||||||
|
"discordClient": "discord",
|
||||||
|
"channelID": "",
|
||||||
|
"color": 16761867
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"plugin": "DiscordRcon",
|
||||||
|
"enabled": false,
|
||||||
|
"discordClient": "discord",
|
||||||
|
"channelID": "",
|
||||||
|
"permissions": {},
|
||||||
|
"prependAdminNameInBroadcast": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"plugin": "TeamRandomizer",
|
"plugin": "TeamRandomizer",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"command": "randomize"
|
"command": "randomize"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"plugin": "SCBLInfo",
|
||||||
|
"enabled": true,
|
||||||
|
"discordClient": "discord",
|
||||||
|
"channelID": "",
|
||||||
|
"threshold": 6
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"logger": {
|
"logger": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "SquadJS",
|
"name": "SquadJS",
|
||||||
"version": "2.0.0-beta1",
|
"version": "2.0.1",
|
||||||
"repository": "https://github.com/Thomas-Smyth/SquadJS.git",
|
"repository": "https://github.com/Thomas-Smyth/SquadJS.git",
|
||||||
"author": "Thomas Smyth <https://github.com/Thomas-Smyth>",
|
"author": "Thomas Smyth <https://github.com/Thomas-Smyth>",
|
||||||
"license": "BSL-1.0",
|
"license": "BSL-1.0",
|
||||||
|
@ -8,7 +8,7 @@ import sequelize from 'sequelize';
|
|||||||
import Logger from 'core/logger';
|
import Logger from 'core/logger';
|
||||||
|
|
||||||
import SquadServer from './index.js';
|
import SquadServer from './index.js';
|
||||||
import plugins from './plugins/index.js';
|
import Plugins from './plugins/index.js';
|
||||||
|
|
||||||
const { Sequelize } = sequelize;
|
const { Sequelize } = sequelize;
|
||||||
|
|
||||||
@ -16,6 +16,8 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|||||||
|
|
||||||
export default class SquadServerFactory {
|
export default class SquadServerFactory {
|
||||||
static async buildFromConfig(config) {
|
static async buildFromConfig(config) {
|
||||||
|
const plugins = await Plugins.getPlugins();
|
||||||
|
|
||||||
for (const plugin of Object.keys(plugins)) {
|
for (const plugin of Object.keys(plugins)) {
|
||||||
Logger.setColor(plugin, 'magentaBright');
|
Logger.setColor(plugin, 'magentaBright');
|
||||||
}
|
}
|
||||||
@ -33,9 +35,6 @@ export default class SquadServerFactory {
|
|||||||
Logger.verbose('SquadServerFactory', 1, 'Creating SquadServer...');
|
Logger.verbose('SquadServerFactory', 1, 'Creating SquadServer...');
|
||||||
const server = new SquadServer(config.server);
|
const server = new SquadServer(config.server);
|
||||||
|
|
||||||
// pull layers read to use to create layer filter connectors
|
|
||||||
await server.squadLayers.pull();
|
|
||||||
|
|
||||||
// initialise connectors
|
// initialise connectors
|
||||||
Logger.verbose('SquadServerFactory', 1, 'Preparing connectors...');
|
Logger.verbose('SquadServerFactory', 1, 'Preparing connectors...');
|
||||||
const connectors = {};
|
const connectors = {};
|
||||||
@ -100,13 +99,6 @@ export default class SquadServerFactory {
|
|||||||
static async createConnector(server, type, connectorName, connectorConfig) {
|
static async createConnector(server, type, connectorName, connectorConfig) {
|
||||||
Logger.verbose('SquadServerFactory', 1, `Starting ${type} connector ${connectorName}...`);
|
Logger.verbose('SquadServerFactory', 1, `Starting ${type} connector ${connectorName}...`);
|
||||||
|
|
||||||
if (type === 'squadlayerpool') {
|
|
||||||
return server.squadLayers[connectorConfig.type](
|
|
||||||
connectorConfig.filter,
|
|
||||||
connectorConfig.activeLayerFilter
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'discord') {
|
if (type === 'discord') {
|
||||||
const connector = new Discord.Client();
|
const connector = new Discord.Client();
|
||||||
await connector.login(connectorConfig);
|
await connector.login(connectorConfig);
|
||||||
@ -160,7 +152,9 @@ export default class SquadServerFactory {
|
|||||||
return SquadServerFactory.buildFromConfigString(SquadServerFactory.readConfigFile(configPath));
|
return SquadServerFactory.buildFromConfigString(SquadServerFactory.readConfigFile(configPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
static buildConfig() {
|
static async buildConfig() {
|
||||||
|
const plugins = await Plugins.getPlugins();
|
||||||
|
|
||||||
const templatePath = path.resolve(__dirname, './templates/config-template.json');
|
const templatePath = path.resolve(__dirname, './templates/config-template.json');
|
||||||
const templateString = fs.readFileSync(templatePath, 'utf8');
|
const templateString = fs.readFileSync(templatePath, 'utf8');
|
||||||
const template = SquadServerFactory.parseConfig(templateString);
|
const template = SquadServerFactory.parseConfig(templateString);
|
||||||
@ -183,13 +177,17 @@ export default class SquadServerFactory {
|
|||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
static buildConfigFile() {
|
static async buildConfigFile() {
|
||||||
const configPath = path.resolve(__dirname, '../config.json');
|
const configPath = path.resolve(__dirname, '../config.json');
|
||||||
const config = JSON.stringify(SquadServerFactory.buildConfig(), null, 2);
|
const config = await SquadServerFactory.buildConfig();
|
||||||
fs.writeFileSync(configPath, config);
|
|
||||||
|
const configString = JSON.stringify(config, null, 2);
|
||||||
|
fs.writeFileSync(configPath, configString);
|
||||||
}
|
}
|
||||||
|
|
||||||
static buildReadmeFile() {
|
static async buildReadmeFile() {
|
||||||
|
const plugins = await Plugins.getPlugins();
|
||||||
|
|
||||||
const pluginKeys = Object.keys(plugins).sort((a, b) =>
|
const pluginKeys = Object.keys(plugins).sort((a, b) =>
|
||||||
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||||
);
|
);
|
||||||
|
@ -6,11 +6,12 @@ import Gamedig from 'gamedig';
|
|||||||
import Logger from 'core/logger';
|
import Logger from 'core/logger';
|
||||||
import { SQUADJS_API_DOMAIN } from 'core/constants';
|
import { SQUADJS_API_DOMAIN } from 'core/constants';
|
||||||
|
|
||||||
|
import { Layers } from './layers/index.js';
|
||||||
|
|
||||||
import LogParser from './log-parser/index.js';
|
import LogParser from './log-parser/index.js';
|
||||||
import Rcon from './rcon.js';
|
import Rcon from './rcon.js';
|
||||||
|
|
||||||
import { SQUADJS_VERSION } from './utils/constants.js';
|
import { SQUADJS_VERSION } from './utils/constants.js';
|
||||||
import { SquadLayers } from './utils/squad-layers.js';
|
|
||||||
|
|
||||||
import fetchAdminLists from './utils/admin-lists.js';
|
import fetchAdminLists from './utils/admin-lists.js';
|
||||||
|
|
||||||
@ -30,11 +31,10 @@ export default class SquadServer extends EventEmitter {
|
|||||||
this.players = [];
|
this.players = [];
|
||||||
|
|
||||||
this.admins = {};
|
this.admins = {};
|
||||||
|
this.adminsInAdminCam = {};
|
||||||
|
|
||||||
this.plugins = [];
|
this.plugins = [];
|
||||||
|
|
||||||
this.squadLayers = new SquadLayers(options.squadLayersSource);
|
|
||||||
|
|
||||||
this.setupRCON();
|
this.setupRCON();
|
||||||
this.setupLogParser();
|
this.setupLogParser();
|
||||||
|
|
||||||
@ -61,7 +61,9 @@ export default class SquadServer extends EventEmitter {
|
|||||||
1,
|
1,
|
||||||
`Beginning to watch ${this.options.host}:${this.options.queryPort}...`
|
`Beginning to watch ${this.options.host}:${this.options.queryPort}...`
|
||||||
);
|
);
|
||||||
await this.squadLayers.pull();
|
|
||||||
|
await Layers.pull();
|
||||||
|
|
||||||
this.admins = await fetchAdminLists(this.options.adminLists);
|
this.admins = await fetchAdminLists(this.options.adminLists);
|
||||||
|
|
||||||
await this.rcon.connect();
|
await this.rcon.connect();
|
||||||
@ -101,6 +103,23 @@ export default class SquadServer extends EventEmitter {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.rcon.on('POSSESSED_ADMIN_CAMERA', async (data) => {
|
||||||
|
data.player = await this.getPlayerBySteamID(data.steamID);
|
||||||
|
|
||||||
|
this.adminsInAdminCam[data.steamID] = data.time;
|
||||||
|
|
||||||
|
this.emit('POSSESSED_ADMIN_CAMERA', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rcon.on('UNPOSSESSED_ADMIN_CAMERA', async (data) => {
|
||||||
|
data.player = await this.getPlayerBySteamID(data.steamID);
|
||||||
|
data.duration = data.time.getTime() - this.adminsInAdminCam[data.steamID].getTime();
|
||||||
|
|
||||||
|
delete this.adminsInAdminCam[data.steamID];
|
||||||
|
|
||||||
|
this.emit('UNPOSSESSED_ADMIN_CAMERA', data);
|
||||||
|
});
|
||||||
|
|
||||||
this.rcon.on('RCON_ERROR', (data) => {
|
this.rcon.on('RCON_ERROR', (data) => {
|
||||||
this.emit('RCON_ERROR', data);
|
this.emit('RCON_ERROR', data);
|
||||||
});
|
});
|
||||||
@ -131,12 +150,21 @@ export default class SquadServer extends EventEmitter {
|
|||||||
this.emit('ADMIN_BROADCAST', data);
|
this.emit('ADMIN_BROADCAST', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logParser.on('NEW_GAME', (data) => {
|
this.logParser.on('DEPLOYABLE_DAMAGED', async (data) => {
|
||||||
data.layer = this.squadLayers.getLayerByLayerClassname(data.layerClassname);
|
data.player = await this.getPlayerByNameSuffix(data.playerSuffix);
|
||||||
|
|
||||||
this.layerHistory.unshift({ ...data.layer, time: data.time });
|
delete data.playerSuffix;
|
||||||
|
|
||||||
|
this.emit('DEPLOYABLE_DAMAGED', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logParser.on('NEW_GAME', async (data) => {
|
||||||
|
data.layer = await Layers.getLayerByClassname(data.layerClassname);
|
||||||
|
|
||||||
|
this.layerHistory.unshift({ layer: data.layer, time: data.time });
|
||||||
this.layerHistory = this.layerHistory.slice(0, this.layerHistoryMaxLength);
|
this.layerHistory = this.layerHistory.slice(0, this.layerHistoryMaxLength);
|
||||||
|
|
||||||
|
this.currentLayer = data.layer;
|
||||||
this.emit('NEW_GAME', data);
|
this.emit('NEW_GAME', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -301,16 +329,21 @@ export default class SquadServer extends EventEmitter {
|
|||||||
Logger.verbose('SquadServer', 1, `Updating layer information...`);
|
Logger.verbose('SquadServer', 1, `Updating layer information...`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const layerInfo = await this.rcon.getLayerInfo();
|
const currentMap = await this.rcon.getCurrentMap();
|
||||||
|
const nextMap = await this.rcon.getNextMap();
|
||||||
|
const nextMapToBeVoted = nextMap === 'To be voted';
|
||||||
|
|
||||||
|
const currentLayer = await Layers.getLayerByName(currentMap.layer);
|
||||||
|
const nextLayer = nextMapToBeVoted ? null : await Layers.getLayerByName(nextMap.layer);
|
||||||
|
|
||||||
if (this.layerHistory.length === 0) {
|
if (this.layerHistory.length === 0) {
|
||||||
const layer = this.squadLayers.getLayerByLayerName(layerInfo.currentLayer);
|
this.layerHistory.unshift({ layer: currentLayer, time: Date.now() });
|
||||||
|
|
||||||
this.layerHistory.unshift({ ...layer, time: Date.now() });
|
|
||||||
this.layerHistory = this.layerHistory.slice(0, this.layerHistoryMaxLength);
|
this.layerHistory = this.layerHistory.slice(0, this.layerHistoryMaxLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nextLayer = layerInfo.nextLayer;
|
this.currentLayer = currentLayer;
|
||||||
|
this.nextLayer = nextLayer;
|
||||||
|
this.nextLayerToBeVoted = nextMapToBeVoted;
|
||||||
|
|
||||||
this.emit('UPDATED_LAYER_INFORMATION');
|
this.emit('UPDATED_LAYER_INFORMATION');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
4
squad-server/layers/index.js
Normal file
4
squad-server/layers/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import Layer from './layer.js';
|
||||||
|
import Layers from './layers.js';
|
||||||
|
|
||||||
|
export { Layer, Layers };
|
47
squad-server/layers/layer.js
Normal file
47
squad-server/layers/layer.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
export default class Layer {
|
||||||
|
constructor(data) {
|
||||||
|
this.name = data.Name;
|
||||||
|
this.classname = data.rawName;
|
||||||
|
this.map = {
|
||||||
|
name: data.mapName
|
||||||
|
};
|
||||||
|
this.gamemode = data.gamemode;
|
||||||
|
this.gamemodeType = data.type;
|
||||||
|
this.version = data.layerVersion;
|
||||||
|
this.size = data.mapSize;
|
||||||
|
this.sizeType = data.mapSizeType;
|
||||||
|
this.numberOfCapturePoints = parseInt(data.capturePoints);
|
||||||
|
this.lighting = {
|
||||||
|
name: data.lighting,
|
||||||
|
classname: data.lightingLevel
|
||||||
|
};
|
||||||
|
this.teams = [
|
||||||
|
{
|
||||||
|
faction: data.team1.faction,
|
||||||
|
name: data.team1.teamSetupName,
|
||||||
|
tickets: data.team1.tickets,
|
||||||
|
commander: data.team1.commander,
|
||||||
|
vehicles: (data.team1.vehicles || []).map((vehicle) => ({
|
||||||
|
name: vehicle.type,
|
||||||
|
classname: vehicle.rawType,
|
||||||
|
count: vehicle.count,
|
||||||
|
spawnDelay: vehicle.delay,
|
||||||
|
respawnDelay: vehicle.respawnTime
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
faction: data.team2.faction,
|
||||||
|
name: data.team2.teamSetupName,
|
||||||
|
tickets: data.team2.tickets,
|
||||||
|
commander: data.team2.commander,
|
||||||
|
vehicles: (data.team2.vehicles || []).map((vehicle) => ({
|
||||||
|
name: vehicle.type,
|
||||||
|
classname: vehicle.rawType,
|
||||||
|
count: vehicle.count,
|
||||||
|
spawnDelay: vehicle.delay,
|
||||||
|
respawnDelay: vehicle.respawnTime
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
53
squad-server/layers/layers.js
Normal file
53
squad-server/layers/layers.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import Logger from 'core/logger';
|
||||||
|
|
||||||
|
import Layer from './layer.js';
|
||||||
|
|
||||||
|
class Layers {
|
||||||
|
constructor() {
|
||||||
|
this.layers = [];
|
||||||
|
|
||||||
|
this.pulled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async pull(force = false) {
|
||||||
|
if (this.pulled && !force) {
|
||||||
|
Logger.verbose('Layers', 1, 'Already pulled layers.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.verbose('Layers', 1, 'Pulling layers...');
|
||||||
|
const response = await axios.get(
|
||||||
|
'https://raw.githubusercontent.com/Squad-Wiki-Editorial/squad-wiki-pipeline-map-data/dev/completed_output/2.0/finished_2.0.json'
|
||||||
|
);
|
||||||
|
|
||||||
|
this.layers = [];
|
||||||
|
for (const layer of response.data.Maps) {
|
||||||
|
this.layers.push(new Layer(layer));
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.verbose('Layers', 1, `Pulled ${this.layers.length} layers.`);
|
||||||
|
|
||||||
|
return this.layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLayerByCondition(condition) {
|
||||||
|
await this.pull();
|
||||||
|
|
||||||
|
const matches = this.layers.filter(condition);
|
||||||
|
if (matches.length === 1) return matches[0];
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLayerByName(name) {
|
||||||
|
return this.getLayerByCondition((layer) => layer.name === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLayerByClassname(classname) {
|
||||||
|
return this.getLayerByCondition((layer) => layer.classname === classname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Layers();
|
20
squad-server/log-parser/deployable-damaged.js
Normal file
20
squad-server/log-parser/deployable-damaged.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export default {
|
||||||
|
regex: /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQDeployable::)?TakeDamage\(\): ([A-z0-9_]+)_C_[0-9]+: ([0-9.]+) damage attempt by causer ([A-z0-9_]+)_C_[0-9]+ instigator (.+) with damage type ([A-z0-9_]+)_C health remaining ([0-9.]+)/,
|
||||||
|
onMatch: (args, logParser) => {
|
||||||
|
const data = {
|
||||||
|
raw: args[0],
|
||||||
|
time: args[1],
|
||||||
|
chainID: args[2],
|
||||||
|
deployable: args[3],
|
||||||
|
damage: parseFloat(args[4]),
|
||||||
|
weapon: args[5],
|
||||||
|
playerSuffix: args[6],
|
||||||
|
damageType: args[7],
|
||||||
|
healthRemaining: args[8]
|
||||||
|
};
|
||||||
|
|
||||||
|
logParser.eventStore[args[3]] = data;
|
||||||
|
|
||||||
|
logParser.emit('DEPLOYABLE_DAMAGED', data);
|
||||||
|
}
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
import LogParser from 'core/log-parser';
|
import LogParser from 'core/log-parser';
|
||||||
|
|
||||||
import AdminBroadcast from './admin-broadcast.js';
|
import AdminBroadcast from './admin-broadcast.js';
|
||||||
|
import DeployableDamaged from './deployable-damaged.js';
|
||||||
import NewGame from './new-game.js';
|
import NewGame from './new-game.js';
|
||||||
import PlayerConnected from './player-connected.js';
|
import PlayerConnected from './player-connected.js';
|
||||||
import PlayerDamaged from './player-damaged.js';
|
import PlayerDamaged from './player-damaged.js';
|
||||||
@ -21,6 +22,7 @@ export default class SquadLogParser extends LogParser {
|
|||||||
getRules() {
|
getRules() {
|
||||||
return [
|
return [
|
||||||
AdminBroadcast,
|
AdminBroadcast,
|
||||||
|
DeployableDamaged,
|
||||||
NewGame,
|
NewGame,
|
||||||
PlayerConnected,
|
PlayerConnected,
|
||||||
PlayerDamaged,
|
PlayerDamaged,
|
||||||
|
@ -443,8 +443,8 @@ export default class DBLog extends BasePlugin {
|
|||||||
dlc: info.dlc,
|
dlc: info.dlc,
|
||||||
mapClassname: info.mapClassname,
|
mapClassname: info.mapClassname,
|
||||||
layerClassname: info.layerClassname,
|
layerClassname: info.layerClassname,
|
||||||
map: info.layer ? info.layer.map : null,
|
map: info.layer ? info.layer.map.name : null,
|
||||||
layer: info.layer ? info.layer.layer : null,
|
layer: info.layer ? info.layer.name : null,
|
||||||
startTime: info.time
|
startTime: info.time
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -31,25 +31,21 @@ export default class DiscordAdminCamLogs extends DiscordBasePlugin {
|
|||||||
|
|
||||||
this.adminsInCam = {};
|
this.adminsInCam = {};
|
||||||
|
|
||||||
this.onPlayerPossess = this.onPlayerPossess.bind(this);
|
this.onEntry = this.onEntry.bind(this);
|
||||||
this.onPlayerUnPossess = this.onPlayerUnPossess.bind(this);
|
this.onExit = this.onExit.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async mount() {
|
async mount() {
|
||||||
this.server.on('PLAYER_POSSESS', this.onPlayerPossess);
|
this.server.on('POSSESSED_ADMIN_CAMERA', this.onEntry);
|
||||||
this.server.on('PLAYER_UNPOSSESS', this.onPlayerUnPossess);
|
this.server.on('UNPOSSESSED_ADMIN_CAMERA', this.onExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
async unmount() {
|
async unmount() {
|
||||||
this.server.removeEventListener('PLAYER_POSSESS', this.onPlayerPossess);
|
this.server.removeEventListener('POSSESSED_ADMIN_CAMERA', this.onEntry);
|
||||||
this.server.removeEventListener('PLAYER_UNPOSSESS', this.onPlayerUnPossess);
|
this.server.removeEventListener('UNPOSSESSED_ADMIN_CAMERA', this.onExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onPlayerPossess(info) {
|
async onEntry(info) {
|
||||||
if (info.player === null || info.possessClassname !== 'CameraMan') return;
|
|
||||||
|
|
||||||
this.adminsInCam[info.player.steamID] = info.time;
|
|
||||||
|
|
||||||
await this.sendDiscordMessage({
|
await this.sendDiscordMessage({
|
||||||
embed: {
|
embed: {
|
||||||
title: `Admin Entered Admin Camera`,
|
title: `Admin Entered Admin Camera`,
|
||||||
@ -71,14 +67,7 @@ export default class DiscordAdminCamLogs extends DiscordBasePlugin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onPlayerUnPossess(info) {
|
async onExit(info) {
|
||||||
if (
|
|
||||||
info.player === null ||
|
|
||||||
info.switchPossess === true ||
|
|
||||||
!(info.player.steamID in this.adminsInCam)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
await this.sendDiscordMessage({
|
await this.sendDiscordMessage({
|
||||||
embed: {
|
embed: {
|
||||||
title: `Admin Left Admin Camera`,
|
title: `Admin Left Admin Camera`,
|
||||||
@ -96,15 +85,11 @@ export default class DiscordAdminCamLogs extends DiscordBasePlugin {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Time in Admin Camera',
|
name: 'Time in Admin Camera',
|
||||||
value: `${Math.round(
|
value: `${Math.round(info.duration / 60000)} mins`
|
||||||
(info.time.getTime() - this.adminsInCam[info.player.steamID].getTime()) / 60000
|
|
||||||
)} mins`
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
timestamp: info.time.toISOString()
|
timestamp: info.time.toISOString()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
delete this.adminsInCam[info.player.steamID];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ export default class DiscordRoundWinner extends DiscordBasePlugin {
|
|||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'Message',
|
name: 'Message',
|
||||||
value: `${info.winner} won on ${info.layer}.`
|
value: `${info.winner} won on ${this.server.layerHistory[1].layer.name}.`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
timestamp: info.time.toISOString()
|
timestamp: info.time.toISOString()
|
||||||
|
@ -69,7 +69,7 @@ export default class DiscordServerStatus extends BasePlugin {
|
|||||||
|
|
||||||
await this.options.discordClient.user.setActivity(
|
await this.options.discordClient.user.setActivity(
|
||||||
`(${this.server.a2sPlayerCount}/${this.server.publicSlots}) ${
|
`(${this.server.a2sPlayerCount}/${this.server.publicSlots}) ${
|
||||||
this.server.layerHistory[0].layer || 'Unknown'
|
this.server.layerHistory[0].layer.name || 'Unknown'
|
||||||
}`,
|
}`,
|
||||||
{ type: 'WATCHING' }
|
{ type: 'WATCHING' }
|
||||||
);
|
);
|
||||||
@ -92,12 +92,15 @@ export default class DiscordServerStatus extends BasePlugin {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Current Layer',
|
name: 'Current Layer',
|
||||||
value: `\`\`\`${this.server.layerHistory[0].layer || 'Unknown'}\`\`\``,
|
value: `\`\`\`${this.server.currentLayer.name || 'Unknown'}\`\`\``,
|
||||||
inline: true
|
inline: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Next Layer',
|
name: 'Next Layer',
|
||||||
value: `\`\`\`${this.server.nextLayer || 'Unknown'}\`\`\``,
|
value: `\`\`\`${
|
||||||
|
this.server.nextLayer?.name ||
|
||||||
|
(this.server.nextLayerToBeVoted ? 'To be voted' : 'Unknown')
|
||||||
|
}\`\`\``,
|
||||||
inline: true
|
inline: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -1,46 +1,41 @@
|
|||||||
import AutoKickUnassigned from './auto-kick-unassigned.js';
|
import fs from 'fs';
|
||||||
import AutoTKWarn from './auto-tk-warn.js';
|
|
||||||
import ChatCommands from './chat-commands.js';
|
|
||||||
import DBLog from './db-log.js';
|
|
||||||
import DiscordAdminBroadcast from './discord-admin-broadcast.js';
|
|
||||||
import DiscordAdminCamLogs from './discord-admin-cam-logs.js';
|
|
||||||
import DiscordAdminRequest from './discord-admin-request.js';
|
|
||||||
import DiscordChat from './discord-chat.js';
|
|
||||||
import DiscordDebug from './discord-debug.js';
|
|
||||||
import DiscordRcon from './discord-rcon.js';
|
|
||||||
import DiscordRoundWinner from './discord-round-winner.js';
|
|
||||||
import DiscordServerStatus from './discord-server-status.js';
|
|
||||||
import DiscordSubsystemRestarter from './discord-subsystem-restarter.js';
|
|
||||||
import DiscordTeamkill from './discord-teamkill.js';
|
|
||||||
import IntervalledBroadcasts from './intervalled-broadcasts.js';
|
|
||||||
import SCBLInfo from './scbl-info.js';
|
|
||||||
import SeedingMode from './seeding-mode.js';
|
|
||||||
import TeamRandomizer from './team-randomizer.js';
|
|
||||||
|
|
||||||
const plugins = [
|
import Logger from 'core/logger';
|
||||||
AutoKickUnassigned,
|
import path from 'path';
|
||||||
AutoTKWarn,
|
import { fileURLToPath } from 'url';
|
||||||
ChatCommands,
|
|
||||||
DBLog,
|
|
||||||
DiscordAdminBroadcast,
|
|
||||||
DiscordAdminCamLogs,
|
|
||||||
DiscordAdminRequest,
|
|
||||||
DiscordChat,
|
|
||||||
DiscordDebug,
|
|
||||||
DiscordRcon,
|
|
||||||
DiscordRoundWinner,
|
|
||||||
DiscordServerStatus,
|
|
||||||
DiscordSubsystemRestarter,
|
|
||||||
DiscordTeamkill,
|
|
||||||
IntervalledBroadcasts,
|
|
||||||
SCBLInfo,
|
|
||||||
SeedingMode,
|
|
||||||
TeamRandomizer
|
|
||||||
];
|
|
||||||
|
|
||||||
const pluginsByName = {};
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
for (const plugin of plugins) {
|
|
||||||
pluginsByName[plugin.name] = plugin;
|
class Plugins {
|
||||||
|
constructor() {
|
||||||
|
this.plugins = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPlugins(force = false) {
|
||||||
|
if (this.plugins && !force) return this.plugins;
|
||||||
|
|
||||||
|
this.plugins = {};
|
||||||
|
|
||||||
|
const dir = await fs.promises.opendir(path.join(__dirname, './'));
|
||||||
|
|
||||||
|
const pluginFilenames = [];
|
||||||
|
for await (const dirent of dir) {
|
||||||
|
if (!dirent.isFile()) continue;
|
||||||
|
if (
|
||||||
|
['index.js', 'base-plugin.js', 'discord-base-plugin.js', 'readme.md'].includes(dirent.name)
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
pluginFilenames.push(dirent.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pluginFilename of pluginFilenames) {
|
||||||
|
Logger.verbose('Plugins', 1, `Loading plugin file ${pluginFilename}...`);
|
||||||
|
const { default: Plugin } = await import(`./${pluginFilename}`);
|
||||||
|
this.plugins[Plugin.name] = Plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.plugins;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default pluginsByName;
|
export default new Plugins();
|
||||||
|
@ -88,11 +88,13 @@ export default class SCBLInfo extends DiscordBasePlugin {
|
|||||||
{ id: info.player.steamID }
|
{ id: info.player.steamID }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!data.steamUser)
|
if (!data.steamUser) {
|
||||||
this.verbose(
|
this.verbose(
|
||||||
2,
|
2,
|
||||||
`Player ${info.name} (Steam ID: ${info.steamID}) is not listed in the Squad Community Ban List.`
|
`Player ${info.name} (Steam ID: ${info.steamID}) is not listed in the Squad Community Ban List.`
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.steamUser.reputationPoints < this.options.threshold) {
|
if (data.steamUser.reputationPoints < this.options.threshold) {
|
||||||
this.verbose(
|
this.verbose(
|
||||||
|
@ -1,29 +1,68 @@
|
|||||||
|
import Logger from 'core/logger';
|
||||||
import Rcon from 'core/rcon';
|
import Rcon from 'core/rcon';
|
||||||
|
|
||||||
export default class SquadRcon extends Rcon {
|
export default class SquadRcon extends Rcon {
|
||||||
processChatPacket(decodedPacket) {
|
processChatPacket(decodedPacket) {
|
||||||
const match = decodedPacket.body.match(
|
const matchChat = decodedPacket.body.match(
|
||||||
/\[(ChatAll|ChatTeam|ChatSquad|ChatAdmin)] \[SteamID:([0-9]{17})] (.+?) : (.*)/
|
/\[(ChatAll|ChatTeam|ChatSquad|ChatAdmin)] \[SteamID:([0-9]{17})] (.+?) : (.*)/
|
||||||
);
|
);
|
||||||
|
if (matchChat) {
|
||||||
|
Logger.verbose('SquadRcon', 2, `Matched chat message: ${decodedPacket.body}`);
|
||||||
|
|
||||||
this.emit('CHAT_MESSAGE', {
|
this.emit('CHAT_MESSAGE', {
|
||||||
raw: decodedPacket.body,
|
raw: decodedPacket.body,
|
||||||
chat: match[1],
|
chat: matchChat[1],
|
||||||
steamID: match[2],
|
steamID: matchChat[2],
|
||||||
name: match[3],
|
name: matchChat[3],
|
||||||
message: match[4],
|
message: matchChat[4],
|
||||||
time: new Date()
|
time: new Date()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchPossessedAdminCam = decodedPacket.body.match(
|
||||||
|
/\[SteamID:([0-9]{17})] (.+?) has possessed admin camera./
|
||||||
|
);
|
||||||
|
if (matchPossessedAdminCam) {
|
||||||
|
Logger.verbose('SquadRcon', 2, `Matched admin camera possessed: ${decodedPacket.body}`);
|
||||||
|
this.emit('POSSESSED_ADMIN_CAMERA', {
|
||||||
|
raw: decodedPacket.body,
|
||||||
|
steamID: matchPossessedAdminCam[1],
|
||||||
|
name: matchPossessedAdminCam[2],
|
||||||
|
time: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchUnpossessedAdminCam = decodedPacket.body.match(
|
||||||
|
/\[SteamID:([0-9]{17})] (.+?) has unpossessed admin camera./
|
||||||
|
);
|
||||||
|
if (matchUnpossessedAdminCam) {
|
||||||
|
Logger.verbose('SquadRcon', 2, `Matched admin camera possessed: ${decodedPacket.body}`);
|
||||||
|
this.emit('UNPOSSESSED_ADMIN_CAMERA', {
|
||||||
|
raw: decodedPacket.body,
|
||||||
|
steamID: matchUnpossessedAdminCam[1],
|
||||||
|
name: matchUnpossessedAdminCam[2],
|
||||||
|
time: new Date()
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async broadcast(message) {
|
async getCurrentMap() {
|
||||||
await this.execute(`AdminBroadcast ${message}`);
|
const response = await this.execute('ShowCurrentMap');
|
||||||
|
const match = response.match(/^Current level is (.*), layer is (.*)/);
|
||||||
|
return { level: match[1], layer: match[2] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLayerInfo() {
|
async getNextMap() {
|
||||||
const response = await this.execute('ShowNextMap');
|
const response = await this.execute('ShowNextMap');
|
||||||
const match = response.match(/^Current map is (.+), Next map is (.*)/);
|
const match = response.match(/^Next level is (.*), layer is (.*)/);
|
||||||
return { currentLayer: match[1], nextLayer: match[2].length === 0 ? null : match[2] };
|
return {
|
||||||
|
level: match[1] !== '' ? match[1] : null,
|
||||||
|
layer: match[2] !== 'To be voted' ? match[2] : null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getListPlayers() {
|
async getListPlayers() {
|
||||||
@ -49,6 +88,10 @@ export default class SquadRcon extends Rcon {
|
|||||||
return players;
|
return players;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async broadcast(message) {
|
||||||
|
await this.execute(`AdminBroadcast ${message}`);
|
||||||
|
}
|
||||||
|
|
||||||
async warn(steamID, message) {
|
async warn(steamID, message) {
|
||||||
await this.execute(`AdminWarn "${steamID}" ${message}`);
|
await this.execute(`AdminWarn "${steamID}" ${message}`);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import SquadServerFactory from '../factory.js';
|
import SquadServerFactory from '../factory.js';
|
||||||
|
|
||||||
console.log('Building config...');
|
console.log('Building config...');
|
||||||
SquadServerFactory.buildConfigFile();
|
SquadServerFactory.buildConfigFile()
|
||||||
console.log('Done.');
|
.then(() => {
|
||||||
|
console.log('Done.');
|
||||||
|
})
|
||||||
|
.catch(console.log);
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import SquadServerFactory from '../factory.js';
|
import SquadServerFactory from '../factory.js';
|
||||||
|
|
||||||
console.log('Building readme...');
|
console.log('Building readme...');
|
||||||
SquadServerFactory.buildReadmeFile();
|
SquadServerFactory.buildReadmeFile()
|
||||||
console.log('Done.');
|
.then(() => {
|
||||||
|
console.log('Done.');
|
||||||
|
})
|
||||||
|
.catch(console.log);
|
||||||
|
@ -22,41 +22,6 @@
|
|||||||
},
|
},
|
||||||
"connectors": {
|
"connectors": {
|
||||||
"discord": "Discord Login Token",
|
"discord": "Discord Login Token",
|
||||||
"squadlayerpool": {
|
|
||||||
"type": "buildPoolFromFilter",
|
|
||||||
"filter": {
|
|
||||||
"whitelistedLayers": null,
|
|
||||||
"blacklistedLayers": null,
|
|
||||||
"whitelistedMaps": null,
|
|
||||||
"blacklistedMaps": null,
|
|
||||||
"whitelistedGamemodes": null,
|
|
||||||
"blacklistedGamemodes": ["Training"],
|
|
||||||
"flagCountMin": null,
|
|
||||||
"flagCountMax": null,
|
|
||||||
"hasCommander": null,
|
|
||||||
"hasTanks": null,
|
|
||||||
"hasHelicopters": null
|
|
||||||
},
|
|
||||||
"activeLayerFilter": {
|
|
||||||
"historyResetTime": 18000000,
|
|
||||||
"layerHistoryTolerance": 8,
|
|
||||||
"mapHistoryTolerance": 4,
|
|
||||||
"gamemodeHistoryTolerance": {
|
|
||||||
"Invasion": 4
|
|
||||||
},
|
|
||||||
"gamemodeRepetitiveTolerance": {
|
|
||||||
"Invasion": 4
|
|
||||||
},
|
|
||||||
"playerCountComplianceEnabled": true,
|
|
||||||
"factionComplianceEnabled": true,
|
|
||||||
"factionHistoryTolerance": {
|
|
||||||
"RUS": 4
|
|
||||||
},
|
|
||||||
"factionRepetitiveTolerance": {
|
|
||||||
"RUS": 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"host": "host",
|
"host": "host",
|
||||||
"port": 3306,
|
"port": 3306,
|
||||||
|
@ -8,7 +8,7 @@ import Logger from 'core/logger';
|
|||||||
const __dirname = fileURLToPath(import.meta.url);
|
const __dirname = fileURLToPath(import.meta.url);
|
||||||
|
|
||||||
export default async function fetchAdminLists(adminLists) {
|
export default async function fetchAdminLists(adminLists) {
|
||||||
Logger.verbose('SquadServer', 2, `Fetching Admin Lists...`);
|
Logger.verbose('SquadServer', 1, `Fetching Admin Lists...`);
|
||||||
|
|
||||||
const groups = {};
|
const groups = {};
|
||||||
const admins = {};
|
const admins = {};
|
||||||
|
@ -1,274 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import didYouMean from 'didyoumean';
|
|
||||||
import fs from 'fs';
|
|
||||||
|
|
||||||
class SquadLayersBase {
|
|
||||||
get layerNames() {
|
|
||||||
return this.layers.map((layer) => layer.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLayerByCondition(condition) {
|
|
||||||
const results = this.layers.filter(condition);
|
|
||||||
return results.length === 1 ? results[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getLayerByLayerName(layerName) {
|
|
||||||
return this.getLayerByCondition((layer) => layer.layer === layerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLayerByLayerClassname(layerClassname) {
|
|
||||||
return this.getLayerByCondition((layer) => layer.layerClassname === layerClassname);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLayerByLayerNameAutoCorrection(layerName) {
|
|
||||||
return this.getLayerByLayerName(didYouMean(layerName, this.layerNames()));
|
|
||||||
}
|
|
||||||
|
|
||||||
getLayerByNumber(layerNumber) {
|
|
||||||
return this.getLayerByCondition((layer) => layer.layerNumber === layerNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SquadLayers extends SquadLayersBase {
|
|
||||||
constructor(source) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.source =
|
|
||||||
source || 'https://raw.githubusercontent.com/Thomas-Smyth/squad-layers/master/layers.json';
|
|
||||||
this.pulled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async pull(force = false) {
|
|
||||||
if (this.pulled && !force) return;
|
|
||||||
|
|
||||||
this.layers = (await axios.get(this.source)).data;
|
|
||||||
for (let i = 0; i < this.layers.length; i++) this.layers[i].layerNumber = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildPoolFromLayerNames(layerNames, activeFilter) {
|
|
||||||
return new SquadLayersPool(
|
|
||||||
this.layers.filter((layer) => layerNames.includes(layer.layer)),
|
|
||||||
activeFilter
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildPoolFromLayerNamesAutoCorrection(layerNames, activeFilter) {
|
|
||||||
return this.buildPoolFromLayerNames(
|
|
||||||
layerNames.map((layerName) => this.getLayerByLayerNameAutoCorrection(layerName)),
|
|
||||||
activeFilter
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildPoolFromFile(path, activeFilter, delimiter = '\n') {
|
|
||||||
return this.buildPoolFromLayerNames(
|
|
||||||
fs.readFileSync(path, 'utf8').split(delimiter),
|
|
||||||
activeFilter
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildPoolFromFilter(filter, activeFilter) {
|
|
||||||
const whitelistedLayers = filter.whitelistedLayers || null;
|
|
||||||
const blacklistedLayers = filter.blacklistedLayers || null;
|
|
||||||
const whitelistedMaps = filter.whitelistedMaps || null;
|
|
||||||
const blacklistedMaps = filter.blacklistedMaps || null;
|
|
||||||
const whitelistedGamemodes = filter.whitelistedGamemodes || null;
|
|
||||||
const blacklistedGamemodes = filter.blacklistedGamemodes || ['Training'];
|
|
||||||
const flagCountMin = filter.flagCountMin || null;
|
|
||||||
const flagCountMax = filter.flagCountMax || null;
|
|
||||||
const hasCommander = filter.hasCommander || null;
|
|
||||||
const hasTanks = filter.hasTanks || null;
|
|
||||||
const hasHelicopters = filter.hasHelicopters || null;
|
|
||||||
|
|
||||||
const layers = [];
|
|
||||||
|
|
||||||
for (const layer of this.layers) {
|
|
||||||
// Whitelist / Blacklist Layers
|
|
||||||
if (whitelistedLayers !== null && !whitelistedLayers.includes(layer.layer)) continue;
|
|
||||||
if (blacklistedLayers !== null && blacklistedLayers.includes(layer.layer)) continue;
|
|
||||||
|
|
||||||
// Whitelist / Blacklist Maps
|
|
||||||
if (whitelistedMaps !== null && !whitelistedMaps.includes(layer.map)) continue;
|
|
||||||
if (blacklistedMaps !== null && blacklistedMaps.includes(layer.map)) continue;
|
|
||||||
|
|
||||||
// Whitelist / Blacklist Gamemodes
|
|
||||||
if (whitelistedGamemodes !== null && !whitelistedGamemodes.includes(layer.gamemode)) continue;
|
|
||||||
if (blacklistedGamemodes !== null && blacklistedGamemodes.includes(layer.gamemode)) continue;
|
|
||||||
|
|
||||||
// Flag Count
|
|
||||||
if (flagCountMin !== null && layer.flagCount < flagCountMin) continue;
|
|
||||||
if (flagCountMax !== null && layer.flagCount > flagCountMax) continue;
|
|
||||||
|
|
||||||
// Other Properties
|
|
||||||
if (hasCommander !== null && layer.commander !== hasCommander) continue;
|
|
||||||
if (hasTanks !== null && (layer.tanks !== 'N/A') !== hasTanks) continue;
|
|
||||||
if (hasHelicopters !== null && (layer.helicopters !== 'N/A') !== hasHelicopters) continue;
|
|
||||||
|
|
||||||
layers.push(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SquadLayersPool(layers, activeFilter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SquadLayersPool extends SquadLayersBase {
|
|
||||||
constructor(layers, activeFilter = null) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.layers = layers;
|
|
||||||
for (let i = 0; i < this.layers.length; i++) this.layers[i].layerNumber = i + 1;
|
|
||||||
this.activeFilter = activeFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
inPool(layer) {
|
|
||||||
if (typeof layer === 'object') layer = layer.layer;
|
|
||||||
return super.layerNames.includes(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
isHistoryCompliant(layerHistory, layer) {
|
|
||||||
if (this.activeFilter === null) return true;
|
|
||||||
|
|
||||||
if (typeof layer === 'object') layer = layer.layer;
|
|
||||||
|
|
||||||
for (
|
|
||||||
let i = 0;
|
|
||||||
i < Math.min(layerHistory.length, this.activeFilter.layerHistoryTolerance);
|
|
||||||
i++
|
|
||||||
) {
|
|
||||||
if (new Date() - layerHistory[i].time > this.activeFilter.historyResetTime) return true;
|
|
||||||
if (layerHistory[i].layer === layer) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
isMapHistoryCompliant(layerHistory, layer) {
|
|
||||||
if (this.activeFilter === null) return true;
|
|
||||||
|
|
||||||
if (typeof layer === 'string') layer = this.getLayerByLayerName(layer);
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.min(layerHistory.length, this.activeFilter.mapHistoryTolerance); i++) {
|
|
||||||
if (new Date() - layerHistory[i].time > this.activeFilter.historyResetTime) return true;
|
|
||||||
if (layerHistory[i].map === layer.map) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
isGamemodeHistoryCompliant(layerHistory, layer) {
|
|
||||||
if (this.activeFilter === null) return true;
|
|
||||||
|
|
||||||
if (typeof layer === 'string') layer = this.getLayerByLayerName(layer);
|
|
||||||
|
|
||||||
const gamemodeHistoryTolerance = this.activeFilter.gamemodeHistoryTolerance[layer.gamemode];
|
|
||||||
if (!gamemodeHistoryTolerance) return true;
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.min(layerHistory.length, gamemodeHistoryTolerance); i++) {
|
|
||||||
if (new Date() - layerHistory[i].time > this.activeFilter.historyResetTime) return true;
|
|
||||||
if (layerHistory[i].gamemode === layer.gamemode) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
isGamemodeRepetitiveCompliant(layerHistory, layer) {
|
|
||||||
if (this.activeFilter === null) return true;
|
|
||||||
|
|
||||||
if (typeof layer === 'string') layer = this.getLayerByLayerName(layer);
|
|
||||||
|
|
||||||
const gamemodeRepetitiveTolerance = this.activeFilter.gamemodeRepetitiveTolerance[
|
|
||||||
layer.gamemode
|
|
||||||
];
|
|
||||||
if (!gamemodeRepetitiveTolerance) return true;
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.min(layerHistory.length, gamemodeRepetitiveTolerance); i++) {
|
|
||||||
if (new Date() - layerHistory[i].time > this.activeFilter.historyResetTime) return true;
|
|
||||||
|
|
||||||
if (layerHistory[i].gamemode.gamemode !== layer.gamemode) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isFactionCompliant(layerHistory, layer) {
|
|
||||||
if (this.activeFilter === null || this.activeFilter.factionComplianceEnabled === false)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (layerHistory.length === 0) return true;
|
|
||||||
|
|
||||||
if (typeof layer === 'string') layer = this.getLayerByLayerName(layer);
|
|
||||||
|
|
||||||
return (
|
|
||||||
!layerHistory[0] ||
|
|
||||||
(layerHistory[0].teamOne.faction !== layer.teamTwo.faction &&
|
|
||||||
layerHistory[0].teamTwo.faction !== layer.teamOne.faction)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
isFactionHistoryCompliant(layerHistory, layer, faction = null) {
|
|
||||||
if (this.activeFilter === null) return true;
|
|
||||||
|
|
||||||
if (typeof layer === 'string') layer = SquadLayers.getLayerByLayerName(layer);
|
|
||||||
|
|
||||||
if (faction === null) {
|
|
||||||
return (
|
|
||||||
this.isFactionHistoryCompliant(layerHistory, layer, layer.teamOne.faction) &&
|
|
||||||
this.isFactionHistoryCompliant(layerHistory, layer, layer.teamTwo.faction)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const factionThreshold = this.activeFilter.factionHistoryTolerance[faction];
|
|
||||||
if (!factionThreshold) return true;
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.min(layerHistory.length, factionThreshold); i++) {
|
|
||||||
if (new Date() - layerHistory[i].time > this.activeFilter.historyResetTime) return true;
|
|
||||||
if (
|
|
||||||
layerHistory[i].teamOne.faction === faction ||
|
|
||||||
layerHistory[i].teamTwo.faction === faction
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isFactionRepetitiveCompliant(layerHistory, layer, faction = null) {
|
|
||||||
if (this.activeFilter === null) return true;
|
|
||||||
|
|
||||||
if (typeof layer === 'string') layer = SquadLayers.getLayerByLayerName(layer);
|
|
||||||
|
|
||||||
if (faction === null) {
|
|
||||||
return (
|
|
||||||
this.isFactionRepetitiveCompliant(layerHistory, layer, layer.teamOne.faction) &&
|
|
||||||
this.isFactionRepetitiveCompliant(layerHistory, layer, layer.teamTwo.faction)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const factionThreshold = this.activeFilter.factionRepetitiveTolerance[faction];
|
|
||||||
if (!factionThreshold) return true;
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.min(layerHistory.length, factionThreshold); i++) {
|
|
||||||
if (new Date() - layerHistory[i].time > this.activeFilter.historyResetTime) return true;
|
|
||||||
|
|
||||||
if (
|
|
||||||
layerHistory[i].teamOne.faction !== faction &&
|
|
||||||
layerHistory[i].teamTwo.faction !== faction
|
|
||||||
)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isPlayerCountCompliant(server, layer) {
|
|
||||||
if (this.activeFilter === null || this.activeFilter.playerCountComplianceEnabled === false)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (typeof layer === 'string') layer = this.getLayerByLayerName(layer);
|
|
||||||
|
|
||||||
return !(
|
|
||||||
server.players.length > layer.estimatedSuitablePlayerCount.max ||
|
|
||||||
server.players.length < layer.estimatedSuitablePlayerCount.min
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { SquadLayers, SquadLayersPool };
|
|
Loading…
Reference in New Issue
Block a user