mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-25 08:49:22 -04:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
297 lines
6.0 KiB
C#
297 lines
6.0 KiB
C#
namespace Sandbox.Services;
|
|
|
|
public struct FindPackageQuery
|
|
{
|
|
public FindPackageQuery()
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Which asset types to find - or null/empty for all
|
|
/// </summary>
|
|
public string Type;
|
|
|
|
/// <summary>
|
|
/// How to sort the list
|
|
/// </summary>
|
|
public SortMode Sort;
|
|
|
|
/// <summary>
|
|
/// SteamId of querying user (should be authenticated!)
|
|
/// </summary>
|
|
public long SteamId;
|
|
|
|
/// <summary>
|
|
/// Get the favourites of this user
|
|
/// </summary>
|
|
public long FavouritesSteamId;
|
|
|
|
/// <summary>
|
|
/// Only return packages in this collection
|
|
/// </summary>
|
|
public string InCollection;
|
|
|
|
/// <summary>
|
|
/// True if we want the total
|
|
/// </summary>
|
|
public bool GetTotalCount;
|
|
|
|
/// <summary>
|
|
/// True if we want facets
|
|
/// </summary>
|
|
public bool GetFacets;
|
|
|
|
/// <summary>
|
|
/// primary asset should match this value
|
|
/// </summary>
|
|
public string PrimaryAsset;
|
|
|
|
/// <summary>
|
|
/// Text search in description, title, summary etc
|
|
/// </summary>
|
|
public string SearchString;
|
|
|
|
/// <summary>
|
|
/// References this package id
|
|
/// </summary>
|
|
public long WithReference;
|
|
|
|
/// <summary>
|
|
/// Org name
|
|
/// </summary>
|
|
public string Org;
|
|
|
|
/// <summary>
|
|
/// Must contain all tags
|
|
/// </summary>
|
|
public List<string> WithTag;
|
|
|
|
/// <summary>
|
|
/// In contest
|
|
/// </summary>
|
|
public string InContest;
|
|
|
|
/// <summary>
|
|
/// Content created for this game, specifically
|
|
/// </summary>
|
|
public string ForGame;
|
|
|
|
/// <summary>
|
|
/// Show hidden packages in this orgs
|
|
/// </summary>
|
|
public long[] OpenOrgs;
|
|
|
|
/// <summary>
|
|
/// If we want to find counter-strike maps, this would be set to the name of the counter-strike package
|
|
/// </summary>
|
|
public string TargetPackage;
|
|
|
|
/// <summary>
|
|
/// Only show packages that we haven't played
|
|
/// </summary>
|
|
public bool Unplayed;
|
|
|
|
/// <summary>
|
|
/// Facets like Category:Wall
|
|
/// </summary>
|
|
public Dictionary<string, string> Facets;
|
|
|
|
public enum SortMode
|
|
{
|
|
Updated,
|
|
Popular,
|
|
Friends,
|
|
Created,
|
|
Random,
|
|
Trending,
|
|
|
|
ThumbsUp,
|
|
ThumbsDown,
|
|
Favourites,
|
|
InCollections,
|
|
|
|
/// <summary>
|
|
/// Order by recently used
|
|
/// </summary>
|
|
Used,
|
|
|
|
RankDay,
|
|
RankWeek,
|
|
RankMonth,
|
|
|
|
Spawns,
|
|
SpawnsDay,
|
|
SpawnsWeek,
|
|
SpawnsMonth,
|
|
|
|
PlayersNow,
|
|
|
|
/// <summary>
|
|
/// No sorting
|
|
/// </summary>
|
|
None
|
|
}
|
|
|
|
public static FindPackageQuery Parse( string query )
|
|
{
|
|
if ( string.IsNullOrWhiteSpace( query ) )
|
|
return default;
|
|
|
|
var find = new FindPackageQuery
|
|
{
|
|
GetTotalCount = true,
|
|
GetFacets = true
|
|
};
|
|
|
|
var tokens = query.ToLowerInvariant().Split( ' ', StringSplitOptions.RemoveEmptyEntries );
|
|
var searchWords = new List<string>();
|
|
var tags = new HashSet<string>();
|
|
|
|
foreach ( var token in tokens )
|
|
{
|
|
// Skip pipe-separated tokens
|
|
if ( token.Contains( '|' ) ) continue;
|
|
|
|
// Handle tag prefixes
|
|
if ( token.StartsWith( '+' ) )
|
|
{
|
|
tags.Add( token[1..] );
|
|
continue;
|
|
}
|
|
|
|
// Skip excluded tags (not currently used in output)
|
|
if ( token.StartsWith( '-' ) ) continue;
|
|
|
|
// Handle key:value pairs
|
|
var colonIndex = token.IndexOf( ':' );
|
|
if ( colonIndex > 0 && colonIndex < token.Length - 1 )
|
|
{
|
|
var key = token[..colonIndex];
|
|
var value = token[(colonIndex + 1)..].Trim();
|
|
|
|
if ( !ProcessToken( ref find, key, value ) )
|
|
{
|
|
// Unknown tokens become facets
|
|
find.Facets ??= new();
|
|
find.Facets[key] = value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Regular search word
|
|
searchWords.Add( token );
|
|
}
|
|
}
|
|
|
|
// Finalize search string and tags
|
|
find.SearchString = string.Join( ' ', searchWords );
|
|
ProcessTags( ref find, tags );
|
|
|
|
return find;
|
|
}
|
|
|
|
private static bool ProcessToken( ref FindPackageQuery find, string key, string value )
|
|
{
|
|
switch ( key )
|
|
{
|
|
case "type":
|
|
find.Type = value;
|
|
return true;
|
|
|
|
case "sort":
|
|
if ( value == "favourite" )
|
|
{
|
|
find.FavouritesSteamId = find.SteamId;
|
|
find.Sort = SortMode.None;
|
|
}
|
|
else
|
|
{
|
|
find.Sort = ParseSortMode( value );
|
|
}
|
|
return true;
|
|
|
|
case "asset":
|
|
find.PrimaryAsset = value.ToLower();
|
|
return true;
|
|
|
|
case "contest":
|
|
find.InContest = value;
|
|
return true;
|
|
|
|
case "in":
|
|
find.InCollection = value;
|
|
return true;
|
|
|
|
case "target":
|
|
find.TargetPackage = value;
|
|
return true;
|
|
|
|
case "org":
|
|
find.Org = value;
|
|
return true;
|
|
|
|
case "is":
|
|
if ( value == "unplayed" )
|
|
find.Unplayed = true;
|
|
// Note: "owner" case was tracked but never used in original
|
|
return true;
|
|
|
|
case "api":
|
|
// API version was parsed but never used in original
|
|
return true;
|
|
|
|
default:
|
|
return false; // Unknown token, will be treated as facet
|
|
}
|
|
}
|
|
|
|
private static void ProcessTags( ref FindPackageQuery find, HashSet<string> tags )
|
|
{
|
|
if ( tags.Count == 0 ) return;
|
|
|
|
// Extract and process game tags
|
|
var gameTag = tags.FirstOrDefault( t => t.StartsWith( "game:" ) );
|
|
if ( gameTag != null )
|
|
{
|
|
var game = gameTag[5..];
|
|
find.ForGame = game == "any" ? null : game;
|
|
tags.Remove( gameTag );
|
|
}
|
|
|
|
// Set remaining tags
|
|
if ( tags.Count > 0 )
|
|
{
|
|
find.WithTag = tags.ToList();
|
|
}
|
|
}
|
|
|
|
private static SortMode ParseSortMode( string sort )
|
|
{
|
|
return sort switch
|
|
{
|
|
"live" or "referenced" or "referencing" or "user" or "used" or "played" => SortMode.Used,
|
|
"oldest" => SortMode.Created, // TODO: might need reverse order flag
|
|
"newest" => SortMode.Created,
|
|
"upvotes" => SortMode.ThumbsUp,
|
|
"downvotes" => SortMode.ThumbsDown,
|
|
"favourite" or "favorites" or "favourites" => SortMode.Favourites,
|
|
"collections" => SortMode.InCollections,
|
|
"friends" => SortMode.Friends,
|
|
"random" => SortMode.Random,
|
|
"popular" => SortMode.Popular,
|
|
"updated" => SortMode.Updated,
|
|
"trending" => SortMode.Trending,
|
|
"rankd" or "rankday" => SortMode.RankDay,
|
|
"rankw" or "rankweek" => SortMode.RankWeek,
|
|
"rankm" or "rankmonth" => SortMode.RankMonth,
|
|
"spawns" => SortMode.Spawns,
|
|
"spawnsday" => SortMode.SpawnsDay,
|
|
"spawnsweek" => SortMode.SpawnsWeek,
|
|
"spawnsmonth" => SortMode.SpawnsMonth,
|
|
"playersnow" => SortMode.PlayersNow,
|
|
_ => SortMode.Popular // Default
|
|
};
|
|
}
|
|
}
|