• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
  • Skip to footer

Tsukasa no Hibi

Cloudy Sky. Occasional Rain.

You are here: Home / Blog / Enabling linked collections in Garry’s Mod dedicated server

Enabling linked collections in Garry’s Mod dedicated server

2014-07-25 by tsukasa Leave a Comment

Ah Garry’s Mod, a game that is both ingenious and infuriating. And it supports the Steam Workshop to add content.

The Workshop has a nice feature called “linked collections” which basically allow you to put all the maps in one collection and all the models into another. You can then add a third collection to link both together. In theory.

Practically this feature does not work in Garry’s Mod, the game processes all items returned from the Workshop API as downloadable content, regardless of whether that’s true or not (hint: if the filetype is 2, it’s a collection, not an addon!).

Being sick of waiting for Facepunch to fix this trivial problem, I figured I could simply rewire the request to another host which will do the pre-processing of collection data for me. The basic idea is that all the contents of collections linked to the primary collection will get “pushed” into the primary collection so Garry’s Mod will be fooled into downloading the contents of three collections “as one”.

Here’s how I did it (warning: Windows only!), I’m sure there are plenty of better ways to go about this:

  1. Install Fiddler, enable traffic capture and customize the rules for OnBeforeRequest by adding code like this:
    // Rewrite the Steam Workshop request for getting collection contents to target our emulator.
    if (oSession.HostnameIs("api.steampowered.com") && (oSession.PathAndQuery=="/ISteamRemoteStorage/GetCollectionDetails/v0001/")) {
      oSession.hostname="example.com";
      oSession.PathAndQuery="/steam_collections.php";
    }
  2. Create a new PHP script named steam_collections.php on your webserver example.com and edit $process_collection to fit your needs:
    <?php
    
    // Prepare the output header!
    header('Content-Type: text/json');
    
    // Only this collection will be processed, all other collections are passed through.
    $process_collection = '123456789';
    
    $api_url = "//api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v0001/";
    
    // These values will be delivered by srcds's POST request.
    $api_key = $_POST['key'];
    $primary_collection = $_POST['publishedfileids'][0];
    $collectioncount = $_POST['collectioncount'];
    $format = $_POST['format'];
    
    // Must be global so every collection can access it.
    $sortorder = 1;
    
    function AddToPrimaryCollection(&$target_collection, $keys_to_add)
    {
     foreach($keys_to_add as &$key)
     {
     $target_collection[] = $key;
     }
    }
    
    function GetCollectionDetails($collection_id, $is_primary_collection = false, $process_children = false)
    {
     global $api_url;
     global $api_key;
     global $collectioncount;
     global $format;
     global $sortorder;
    
     $final_data = array();
    
     $post_fields = array(
     'collectioncount' => $collectioncount,
     'publishedfileids[0]' => $collection_id,
     'key' => $api_key,
     'format' => $format
     );
    
     $post_options = array(
     'http' => array(
     'header' => "Content-type: application/x-www-form-urlencoded\r\n",
     'method' => 'POST',
     'content' => http_build_query($post_fields),
     'timeout' => 120
     ),
     );
    
     $request_context = stream_context_create($post_options);
     $request_result = file_get_contents($api_url, false, $request_context);
     $json_data = json_decode($request_result, true);
    
     if($process_children)
     {
     if ($is_primary_collection)
     {
    
     foreach ($json_data['response']['collectiondetails'][0]['children'] as $key => &$collection_item) {
     if ($collection_item['filetype'] == '2')
     {
     // Grab the subcollection contents and add them to the mix list
     $sub_collection = GetCollectionDetails($collection_item['publishedfileid'], false, false);
     AddToPrimaryCollection($final_data, $sub_collection['response']['collectiondetails'][0]['children']);
    
     // Get rid of the collection reference
     unset($json_data['response']['collectiondetails'][0]['children'][$key]);
     }
     }
    
     // Now mix the aggregated list of all subcollections with the primary collection
     AddToPrimaryCollection($final_data, $json_data['response']['collectiondetails'][0]['children']);
     $json_data['response']['collectiondetails'][0]['children'] = $final_data;
     }
     
     // When in the primary collection, return the merged data array.
     if ($is_primary_collection)
     {
     foreach ($json_data['response']['collectiondetails'][0]['children'] as $key => &$collection_item)
     {
     $collection_item['sortorder'] = $sortorder;
     $sortorder += 1;
     }
     }
     }
    
     return $json_data;
    }
    
    if($primary_collection == $process_collection)
     // It's our target collection with subcollections, process it!
     $result = GetCollectionDetails($primary_collection, true, true);
    else
     // It's something else... don't bother!
     $result = GetCollectionDetails($primary_collection, true, false);
    
    // Now encode the data back to json and let srcds do it's thing...
    echo json_encode($result);
    
    ?>
  3. Launch srcds with the +host_workshop_collection 123456789 parameter and watch the magic happen. The start might take a little longer than usually.

It would be really nice if this would finally get fixed, it has been reported ages ago.

Filed Under: Blog

Reader Interactions

Leave a ReplyCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Primary Sidebar

Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here: Cookie Policy

Tags

.net AutoHotkey Bitcasa Blog C# Docker Fonts Fun Google Chrome Hardware How To Life Linux Mono Mplayer Music My NAS nVidia OBS OpenEdge OpenSSH Palaver Pangya! Portable App PowerShell Programming Quote RaiDrive Reminder Scripting Software Streaming Technology Tips twitter Video Virtualization VMware Web Windows Wine WordPress Wuala YouTube ZNC

Cool stuff

  • AdiIRC
  • Affinity Designer
  • Affinity Photo
  • AquaSnap
  • Arch Linux
  • Archive Team
  • ConEmu
  • Debian
  • Directory Opus
  • Far Manager
  • FileLocator Pro
  • Fluent Search
  • foobar2000
  • Forte Agent
  • IRCCloud
  • ISBoxer
  • Jetbrains Rider
  • Newsblur
  • OBS Studio
  • Obsidian.md
  • RaiDrive
  • Sublime
  • SyncBackPro
  • The Semware Editor
  • True Launch Bar
  • Vivaldi
  • Wavebox
  • WinHex
  • WinRAR
  • WinSCP
  • XYplorer
  • ZNC
  • Zoom Player Max

Semantic Web

  • Mastodon
  • Tsukasa no Hibi
  • Tsukasa no Hibi Sitemap

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Footer

About

Going since 2004, Tsukasa no Hibi is my personal blog about technology, media and sometimes society.

More about Tsukasa no Hibi

WordPress · Log in