{"id":14965028,"url":"https://github.com/socksthewolf/l4d2bridge","last_synced_at":"2026-03-02T11:01:35.522Z","repository":{"id":254533946,"uuid":"835534220","full_name":"SocksTheWolf/L4D2Bridge","owner":"SocksTheWolf","description":"Allows outside sources to influence and modify a L4D2 server based on user-defined rules using data from events via external sources","archived":false,"fork":false,"pushed_at":"2025-09-02T14:28:02.000Z","size":860,"stargazers_count":0,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-25T10:35:28.687Z","etag":null,"topics":["avalonia","avaloniaui","l4d2","l4d2-coop-plugins","l4d2-server","left4dead2","rcon","sourcemod","sourcemod-plugin","sourcemod-plugins","srcds-rcon","tiltify","twitch","twitchlib","twitchtv"],"latest_commit_sha":null,"homepage":"https://wolf.stream","language":"SourcePawn","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SocksTheWolf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"ko_fi":"socksthewolf"}},"created_at":"2024-07-30T03:22:10.000Z","updated_at":"2025-05-17T20:42:30.000Z","dependencies_parsed_at":"2024-08-24T05:43:50.938Z","dependency_job_id":"3b9b354f-9f82-40d4-80df-2f3d8e043205","html_url":"https://github.com/SocksTheWolf/L4D2Bridge","commit_stats":{"total_commits":109,"total_committers":2,"mean_commits":54.5,"dds":0.02752293577981646,"last_synced_commit":"69ac1636f12044f5a946fd00b4ded193fa9fafb2"},"previous_names":["socksthewolf/l4d2bridge"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/SocksTheWolf/L4D2Bridge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SocksTheWolf%2FL4D2Bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SocksTheWolf%2FL4D2Bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SocksTheWolf%2FL4D2Bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SocksTheWolf%2FL4D2Bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SocksTheWolf","download_url":"https://codeload.github.com/SocksTheWolf/L4D2Bridge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SocksTheWolf%2FL4D2Bridge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29999217,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T09:59:02.300Z","status":"ssl_error","status_checked_at":"2026-03-02T09:59:02.001Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["avalonia","avaloniaui","l4d2","l4d2-coop-plugins","l4d2-server","left4dead2","rcon","sourcemod","sourcemod-plugin","sourcemod-plugins","srcds-rcon","tiltify","twitch","twitchlib","twitchtv"],"created_at":"2024-09-24T13:34:07.242Z","updated_at":"2026-03-02T11:01:35.029Z","avatar_url":"https://github.com/SocksTheWolf.png","language":"SourcePawn","funding_links":["https://ko-fi.com/socksthewolf"],"categories":[],"sub_categories":[],"readme":"# L4D2 Bridge\n\nThis is an application and service layer to a L4D2 server, that allows for external events to influence an active server live.\n\n![Main Window](./image.png)\n\n## Features\n\n* Live server remote console\n* Robust action handling\n* Multiple service event ingesting\n* Fairly lightweight\n* History recall\n* High flexibility with actions\n* Configurable\n\n### Console Functionality\n\nAnything typed in the text box will be sent directly to the server via RCON, with the following exceptions:\n\n* `reload` - reloads the configuration of the application\n* `clear`/`cls` - clears the console log entirely\n* `pause`/`unpause`/`resume` - if the test service is running, will pause/unpause the test service from generating events\n* `cancel` - cancel all current command events\n* `respawn` - respawns all players\n* `action \u003cactionname\u003e \u003cdonor\u003e` - runs this action on the server\n* `commands` - prints the number of events in the queue\n* `help` - prints out these commands directly to the console\n\n## Influence Service Sources\n\n* Tiltify Donations\n* Twitch Events (Potentially Broken ATM)\n* Twitch Donations\n\n## Setup\n\n### Configuration\n\nAll configuration data is stored in a flat file called `config.json`. Upon first time running the application, a blank `config.json` file will be created for you.\nFor the most part, these settings are fairly straight forward to fill out. The rest of the sections are explained below.\n\n#### Actions\n\nThe actions section is a dictionary with key names that correspond to the `RuleName` or `SuccessEvent` of a matching rule definition (see Rules section), and the string array \nof server commands that should be ran when rules with the given keyname matches.\n\nAcceptable server actions are:\n\n```\n    SpawnTank,\n    SpawnSpitter,\n    SpawnJockey,\n    SpawnWitch,\n    SpawnMob,\n    SpawnMobSmall,\n    SpawnMobMedium,\n    SpawnMobLarge,\n    SpawnBoomer,\n    SpawnHunter,\n    SpawnCharger,\n    SpawnSmoker,\n    Lootbox,\n    SupplyCrate,\n    HealAllPlayersSmall,\n    HealAllPlayersLarge,\n    HealAllPlayersRand,\n    RespawnAllPlayers,\n    UppiesPlayers,\n    RandomPositive,\n    RandomNegative,\n    RandomSpecialInfected,\n    Random\n```\n\nThe `Random` server action does a coin flip and if heads, will run a `RandomPositive` action, if tails, a `RandomNegative` action will execute instead.\n\n##### Example\n\nHere is an example of some actions that are defined in a config file. Names such as \"chaos\" and \"santa\" are used in the `rules.json` later.\n\n```\n  \"Actions\": {\n    \"chaos\": [\n      \"SpawnMobLarge\",\n      \"RandomSpecialInfected\",\n      \"SpawnCharger\"\n    ],\n    \"tank\": [\n      \"SpawnTank\"\n    ],\n    \"lootbox\": [\n      \"Lootbox\"\n    ],\n    \"santa\": [\n      \"Lootbox\",\n      \"Lootbox\",\n      \"SupplyCrate\"\n    ]\n  },\n```\n\n#### Mob Sizes\n\nThese are the ranges for each type of mob spawn size in the server commands list. The `Rand` setting is when the mob size is not provided (using `SpawnMob`).\n\n#### Negative Weights\n\nThese are the weights for each of the negative-based Actions with their weightings from 1-100 on how often they should appear. These are used to calculate what affect is\nused when a rule with the action `RandomNegative` is executed. If a negative action is not specified, it will not be randomly chosen when `RandomNegative` is executed. This field is also used to determine `RandomSepcialInfected` roll spawn chances.\n\n---\n\n### Rules\n\n`rules.json` is an [MS RulesEngine](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#rules-schema) formatted file. \n\nThe following important notes are:\n\n* `WorkflowName` has to be one of the services that this application supports. Currently `tiltify` or `twitch`\n* For each rule, either `RuleName` or `SuccessEvent` should be the name of one of the actions you have defined in the `config.json` file earlier. The system will check for `RuleName` matches first and upon failure to match, will check the `SuccessEvent` for an action match.\n* All rules are evaluated for every event of a given service type. Execution does not stop on the first rule event, so you can have mutliple rules running at once.\n* Rules run every time against a given source event.\n\n#### Source Event Types\n\nRules can check their input object types against these values to determine actions. These are the types that are supported:\n\n```\n    Donation,\n    Subscription,\n    Resubscription,\n    GiftSubscription,\n    MultiGiftSubscription,\n    Raid,\n    ChatCommand\n```\n\n#### Source Event Data\n\nAll input objects are data objects that contain the following fields that can be checked against:\n\n* Type - the Source Event Types listed above\n* Name - The user that caused this event to fire\n* Channel - The twitch channel this occurred on (tiltify does not pass this data)\n* Amount - The numerical amount of whatever was passed.\n* Message - Any messages attached (if supported)\n\n\n#### Rules Extensions\n\nThe rules can take advantage of some added extensions to the processor to do string based checks. A class named `REUtils` is provided to the RulesEngine to help you with string processing.\n\n* `REUtils.HasValue` - Returns a boolean if the given string has an actual value instead of null/whitespace\n* `REUtils.CheckContains` - Given the input and a string of a value or csv of values, will check if the input contains any of the values in the check. All values will be projected to case-insensitive checks.\n* `REUtils.PercentChance` - Given a whole number percentage chance, if the RNG flip is that percentage or lower, returns true\n\n#### Example\n\n```\n[\n  {\n    \"WorkflowName\": \"tiltify\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"tank\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.Type == EventType.Donation AND input1.Amount \u003e= 5.00\"\n      },\n      {\n        \"RuleName\": \"santa\",\n        \"SuccessEvent\": \"nothing\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.Type == EventType.Donation AND REutils.CheckContains(input1.Message, \\\"help,santa,save\\\") == true AND input1.Amount \u003e 1.00\"\n      },\n      {\n        \"RuleName\": \"EveryCoolAndAwesomeRuleName\",\n        \"SuccessEvent\": \"lootbox\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.Type == EventType.Donation\"\n      }\n    ]\n  }\n]\n```\n\n---\n\n### Server Dependencies\n\nIn addition to the modifications made in the L4D2Mods directory, the following additional plugins are needed.\n\n* [Left 4 DHooks Direct](https://forums.alliedmods.net/showthread.php?t=321696) by Silvers.\n* [Weapon Handling](https://forums.alliedmods.net/showthread.php?t=319947) by Lux.\n* [Survivor Utilities](https://forums.alliedmods.net/showthread.php?t=335683) by Eärendil.\n* [Explosive Shots](https://forums.alliedmods.net/showthread.php?t=342301) by Eärendil.\n* [multicolors](https://github.com/fbef0102/L4D1_2-Plugins/releases/tag/Multi-Colors) by Bara, et all.\n\n---\n\n### Prepackaged Server Setup\n\n[![LEFT4MAW Event Thumbnail](https://img.youtube.com/vi/-1qD6SKd-SQ/0.jpg)](https://www.youtube.com/watch?v=-1qD6SKd-SQ)\n\nIf you would like to deploy this on a server you maintain, you can use the predefined server package that holds up to 20 players (used for the Left4MAW event seen above) from [here](https://storage.socksthewolf.com/L4D2ServerImage.zip). The bridge config that was used can also be obtained from [here](https://storage.socksthewolf.com/L4D2BridgeConfigs.zip) as well.\n\nI'd love to hear about your usage of this project, so please contact me over on [bluesky](https://bsky.app/profile/socksthewolf.com) about how you use this for your event!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsocksthewolf%2Fl4d2bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsocksthewolf%2Fl4d2bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsocksthewolf%2Fl4d2bridge/lists"}