diff --git a/README.md b/README.md index 7abd16a..6a977b6 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Carsa's Commands v2 can be subscribed to on the [Steam Workshop](https://steamco ### Setting an Owner To override the automatic assigning of an owner when the very first unique player joins, you can do the following: 1. Open `script.lua` (can be found in the location from step 2 of [the setup](#dedicated-server-setup)) -2. On line 13, you should find the following: +2. On line 16, you should find the following: ```lua local OWNER_STEAM_ID = "0" ``` diff --git a/dev/serverDefinitions.lua b/dev/serverDefinitions.lua index 77d2b52..8c6aa33 100644 --- a/dev/serverDefinitions.lua +++ b/dev/serverDefinitions.lua @@ -1,9 +1,9 @@ -- Stormworks Addon Lua Definitions -- Stormworks v1.4.7 64bit --- Recommended extension: https://marketplace.visualstudio.com/items?itemName=sumneko.lua +-- Recommended VSCode extension: https://marketplace.visualstudio.com/items?itemName=sumneko.lua ----@diagnostic disable: lowercase-global, unused-local, undefined-global +---@diagnostic disable: lowercase-global, unused-local, undefined-global, missing-return ---Persistent data table saved to the world's save file g_savedata = {} @@ -12,305 +12,334 @@ server = {} matrix = {} property = {} --- Types Aliases -- ---#region +--#region Types Aliases + +---@alias fraction number [0..1] Number between 0 and 1 (inclusive) +---@alias currency number + +---@alias Peer_ID integer +---@alias Steam_ID string +---@alias Vehicle_ID integer +---@alias Object_ID integer +---@alias UI_ID integer + + +---@class Transform +---@field [ 1] number|1 Rotation and scale data +---@field [ 2] number Rotation and scale data +---@field [ 3] number Rotation and scale data +---@field [ 4] 0 +---@field [ 5] number Rotation and scale data +---@field [ 6] number|1 Rotation and scale data +---@field [ 7] number Rotation and scale data +---@field [ 8] 0 +---@field [ 9] number Rotation and scale data +---@field [10] number Rotation and scale data +---@field [11] number|1 Rotation and scale data +---@field [12] 0 +---@field [13] number Position X (X on the in-game map) +---@field [14] number Position Y Altitude +---@field [15] number Position Z (Y on the in-game map) +---@field [16] 1 ----@alias peer_id number ----@alias vehicle_id number ----@alias object_id number ---@alias NOTIFICATION_TYPE ----| '0' # new_mission ----| '1' # new_mission_critical ----| '2' # failed_mission ----| '3' # failed_mission_critical ----| '4' # complete_mission ----| '5' # network_connect ----| '6' # network_disconnect ----| '7' # network_info ----| '8' # chat_message ----| '9' # network_info_critical +---|0 # new_mission +---|1 # new_mission_critical +---|2 # failed_mission +---|3 # failed_mission_critical +---|4 # complete_mission +---|5 # network_connect +---|6 # network_disconnect +---|7 # network_info +---|8 # chat_message +---|9 # network_info_critical ---@alias TYPE_STRING ----| '"zone"' # Mission zone type ----| '"object"' # Object type. Gets object_id ----| '"character"' # Character type. Gets object_id ----| '"vehicle"' # Vehicle type. Gets vehicle_id ----| '"flare"' # Flare type. Gets object_id ----| '"fire"' # Fire type. Gets object_id ----| '"loot"' # Loot type. Gets object_id ----| '"button"' # Button type. Gets object_id ----| '"animal"' # Animal type. Gets object_id ----| '"ice"' # Ice type. Gets object_id +---| "zone" # Mission zone type +---| "object" # Object type. Gets object_id +---| "character" # Character type. Gets object_id +---| "vehicle" # Vehicle type. Gets vehicle_id +---| "flare" # Flare type. Gets object_id +---| "fire" # Fire type. Gets object_id +---| "loot" # Loot type. Gets object_id +---| "button" # Button type. Gets object_id +---| "animal" # Animal type. Gets object_id +---| "ice" # Ice type. Gets object_id ---@alias POSITION_TYPE ----| '0' # fixed ----| '1' # vehicle ----| '2' # object +---|0 # fixed +---|1 # vehicle +---|2 # object ---@alias MARKER_TYPE ----| '0' # Delivery_target ----| '1' # Survivor ----| '2' # Object ----| '3' # Waypoint ----| '4' # Tutorial ----| '5' # Fire ----| '6' # Shark ----| '7' # Ice ----| '8' # Search_radius ----| '9' # Flag_1 ----| '10' # Flag_2 ----| '11' # House ----| '12' # Car ----| '13' # Plane ----| '14' # Tank ----| '15' # Heli ----| '16' # Ship ----| '17' # Boat ----| '18' # Attack ----| '19' # Defend +---|0 # Delivery_target +---|1 # Survivor +---|2 # Object +---|3 # Waypoint +---|4 # Tutorial +---|5 # Fire +---|6 # Shark +---|7 # Ice +---|8 # Search_radius +---|9 # Flag_1 +---|10 # Flag_2 +---|11 # House +---|12 # Car +---|13 # Plane +---|14 # Tank +---|15 # Heli +---|16 # Ship +---|17 # Boat +---|18 # Attack +---|19 # Defend ---@alias LABEL_TYPE ----|'0' # None ----|'1' # Cross ----|'2' # Wreckage ----|'3' # Terminal ----|'4' # Military ----|'5' # Heritage ----|'6' # Rig ----|'7' # Industrial ----|'8' # Hospital ----|'9' # Science ----|'10' # Airport ----|'11' # Coastguard ----|'12' # Lighthouse ----|'13' # Fuel ----|'14' # Fuel_sell +---|0 # None +---|1 # Cross +---|2 # Wreckage +---|3 # Terminal +---|4 # Military +---|5 # Heritage +---|6 # Rig +---|7 # Industrial +---|8 # Hospital +---|9 # Science +---|10 # Airport +---|11 # Coastguard +---|12 # Lighthouse +---|13 # Fuel +---|14 # Fuel_sell ---@alias OBJECT_TYPE ----| '0' # None ----| '1' # Character ----| '2' # Crate_small ----| '3' # Collectable ----| '4' # Basketball ----| '5' # Television ----| '6' # Barrel ----| '7' # Schematic ----| '8' # Debris ----| '9' # Chair ----| '10' # Trolley_food ----| '11' # Trolley_med ----| '12' # Clothing ----| '13' # Office_chair ----| '14' # Book ----| '15' # Bottle ----| '16' # Fryingpan ----| '17' # Mug ----| '18' # Saucepan ----| '19' # Stool ----| '20' # Telescope ----| '21' # Log ----| '22' # Bin ----| '23' # Book_2 ----| '24' # Loot ----| '25' # Blue_barrel ----| '26' # Buoyancy_ring ----| '27' # Container ----| '28' # Gas_canister ----| '29' # Pallet ----| '30' # Storage_bin ----| '31' # Fire_extinguisher ----| '32' # Trolley_tool ----| '33' # Cafetiere ----| '34' # Drawers_tools ----| '35' # Glass ----| '36' # Microwave ----| '37' # Plate ----| '38' # Box_closed ----| '39' # Box_open ----| '40' # Desk_lamp ----| '41' # Eraser_board ----| '42' # Folder ----| '43' # Funnel ----| '44' # Lamp ----| '45' # Microscope ----| '46' # Notebook ----| '47' # Pen_marker ----| '48' # Pencil ----| '49' # Scales ----| '50' # Science_beaker ----| '51' # Science_cylinder ----| '52' # Science_flask ----| '53' # Tub_1 ----| '54' # Tub_2 ----| '55' # Filestack ----| '56' # Barrel_toxic ----| '57' # Flare ----| '58' # Fire ----| '59' # Animal ----| '60' # Map_label ----| '61' # Iceberg ----| '62' # Small_flare ----| '63' # Big_flare +---|0 # None +---|1 # Character +---|2 # Crate_small +---|3 # Collectable +---|4 # Basketball +---|5 # Television +---|6 # Barrel +---|7 # Schematic +---|8 # Debris +---|9 # Chair +---|10 # Trolley_food +---|11 # Trolley_med +---|12 # Clothing +---|13 # Office_chair +---|14 # Book +---|15 # Bottle +---|16 # Fryingpan +---|17 # Mug +---|18 # Saucepan +---|19 # Stool +---|20 # Telescope +---|21 # Log +---|22 # Bin +---|23 # Book_2 +---|24 # Loot +---|25 # Blue_barrel +---|26 # Buoyancy_ring +---|27 # Container +---|28 # Gas_canister +---|29 # Pallet +---|30 # Storage_bin +---|31 # Fire_extinguisher +---|32 # Trolley_tool +---|33 # Cafetiere +---|34 # Drawers_tools +---|35 # Glass +---|36 # Microwave +---|37 # Plate +---|38 # Box_closed +---|39 # Box_open +---|40 # Desk_lamp +---|41 # Eraser_board +---|42 # Folder +---|43 # Funnel +---|44 # Lamp +---|45 # Microscope +---|46 # Notebook +---|47 # Pen_marker +---|48 # Pencil +---|49 # Scales +---|50 # Science_beaker +---|51 # Science_cylinder +---|52 # Science_flask +---|53 # Tub_1 +---|54 # Tub_2 +---|55 # Filestack +---|56 # Barrel_toxic +---|57 # Flare +---|58 # Fire +---|59 # Animal +---|60 # Map_label +---|61 # Iceberg +---|62 # Small_flare +---|63 # Big_flare ---@alias OUTFIT_TYPE ----| '0' # None ----| '1' # Worker ----| '2' # Fishing ----| '3' # Waiter ----| '4' # Swimsuit ----| '5' # Military ----| '6' # Office ----| '7' # Police ----| '8' # Science ----| '9' # Medical ----| '10' # Wetsuit ----| '11' # Civilian +---|0 # None +---|1 # Worker +---|2 # Fishing +---|3 # Waiter +---|4 # Swimsuit +---|5 # Military +---|6 # Office +---|7 # Police +---|8 # Science +---|9 # Medical +---|10 # Wetsuit +---|11 # Civilian ---@alias ANIMAL_TYPE ----| '0' # Shark ----| '1' # Whale ----| '2' # Seal ----| '3' # Penguin +---|0 # Shark +---|1 # Whale +---|2 # Seal +---|3 # Penguin ---@alias EQUIPMENT_ID ----| '0' # None ----| '1' # Diving ----| '2' # Firefighter ----| '3' # Scuba ----| '4' # Parachute [int = {0 = deployed, 1 = ready}] ----| '5' # Arctic ----| '6' # Binoculars ----| '7' # Cable ----| '8' # Compass ----| '9' # Defibrillator [int = charges] ----| '10' # Fire_extinguisher [float = ammo] ----| '11' # First_aid [int = charges] ----| '12' # Flare [int = charges] ----| '13' # Flaregun [int = ammo] ----| '14' # Flaregun_ammo [int = ammo] ----| '15' # Flashlight [float = battery] ----| '16' # Hose [int = {0 = hose off, 1 = hose on}] ----| '17' # Night_vision_binoculars [float = battery] ----| '18' # Oxygen_mask [float = oxygen] ----| '19' # Radio [int = channel] [float = battery] ----| '20' # Radio_signal_locator [float = battery] ----| '21' # Remote_control [int = channel] [float = battery] ----| '22' # Rope ----| '23' # Strobe_light [int = {0 = off, 1 = on}] [float = battery] ----| '24' # Strobe_light_infrared [int = {0 = off, 1 = on}] [float = battery] ----| '25' # Transponder [int = {0 = off, 1 = on}] [float = battery] ----| '26' # Underwater_welding_torch [float = charge] ----| '27' # Welding_torch [float = charge] ----| '28' # Coal ----| '29' # Hazmat ----| '30' # Radiation_detector [float = battery] ----| '31' # C4 [int = ammo] ----| '32' # C4_detonator ----| '33' # Speargun [int = ammo] ----| '34' # Speargun_ammo ----| '35' # Pistol [int = ammo] ----| '36' # Pistol_ammo ----| '37' # Smg [int = ammo] ----| '38' # Smg_ammo ----| '39' # Rifle [int = ammo] ----| '40' # Rifle_ammo ----| '41' # Grenade [int = ammo] ----| '42' # Machine_gun_ammo_box_k ----| '43' # Machine_gun_ammo_box_he ----| '44' # Machine_gun_ammo_box_he_frag ----| '45' # Machine_gun_ammo_box_ap ----| '46' # Machine_gun_ammo_box_i ----| '47' # Light_auto_ammo_box_k ----| '48' # Light_auto_ammo_box_he ----| '49' # Light_auto_ammo_box_he_frag ----| '50' # Light_auto_ammo_box_ap ----| '51' # Light_auto_ammo_box_i ----| '52' # Rotary_auto_ammo_box_k ----| '53' # Rotary_auto_ammo_box_he ----| '54' # Rotary_auto_ammo_box_he_frag ----| '55' # Rotary_auto_ammo_box_ap ----| '56' # Rotary_auto_ammo_box_i ----| '57' # Heavy_auto_ammo_box_k ----| '58' # Heavy_auto_ammo_box_he ----| '59' # Heavy_auto_ammo_box_he_frag ----| '60' # Heavy_auto_ammo_box_ap ----| '61' # Heavy_auto_ammo_box_i ----| '62' # Battle_shell_k ----| '63' # Battle_shell_he ----| '64' # Battle_shell_he_frag ----| '65' # Battle_shell_ap ----| '66' # Battle_shell_i ----| '67' # Artillery_shell_k ----| '68' # Artillery_shell_he ----| '69' # Artillery_shell_he_frag ----| '70' # Artillery_shell_ap ----| '71' # Artillery_shell_i +---|0 # None +---|1 # Diving +---|2 # Firefighter +---|3 # Scuba +---|4 # Parachute [int = {0 = deployed, 1 = ready}] +---|5 # Arctic +---|6 # Binoculars +---|7 # Cable +---|8 # Compass +---|9 # Defibrillator [int = charges] +---|10 # Fire_extinguisher [float = ammo] +---|11 # First_aid [int = charges] +---|12 # Flare [int = charges] +---|13 # Flaregun [int = ammo] +---|14 # Flaregun_ammo [int = ammo] +---|15 # Flashlight [float = battery] +---|16 # Hose [int = {0 = hose off, 1 = hose on}] +---|17 # Night_vision_binoculars [float = battery] +---|18 # Oxygen_mask [float = oxygen] +---|19 # Radio [int = channel] [float = battery] +---|20 # Radio_signal_locator [float = battery] +---|21 # Remote_control [int = channel] [float = battery] +---|22 # Rope +---|23 # Strobe_light [int = {0 = off, 1 = on}] [float = battery] +---|24 # Strobe_light_infrared [int = {0 = off, 1 = on}] [float = battery] +---|25 # Transponder [int = {0 = off, 1 = on}] [float = battery] +---|26 # Underwater_welding_torch [float = charge] +---|27 # Welding_torch [float = charge] +---|28 # Coal +---|29 # Hazmat +---|30 # Radiation_detector [float = battery] +---|31 # C4 [int = ammo] +---|32 # C4_detonator +---|33 # Speargun [int = ammo] +---|34 # Speargun_ammo +---|35 # Pistol [int = ammo] +---|36 # Pistol_ammo +---|37 # Smg [int = ammo] +---|38 # Smg_ammo +---|39 # Rifle [int = ammo] +---|40 # Rifle_ammo +---|41 # Grenade [int = ammo] +---|42 # Machine_gun_ammo_box_k +---|43 # Machine_gun_ammo_box_he +---|44 # Machine_gun_ammo_box_he_frag +---|45 # Machine_gun_ammo_box_ap +---|46 # Machine_gun_ammo_box_i +---|47 # Light_auto_ammo_box_k +---|48 # Light_auto_ammo_box_he +---|49 # Light_auto_ammo_box_he_frag +---|50 # Light_auto_ammo_box_ap +---|51 # Light_auto_ammo_box_i +---|52 # Rotary_auto_ammo_box_k +---|53 # Rotary_auto_ammo_box_he +---|54 # Rotary_auto_ammo_box_he_frag +---|55 # Rotary_auto_ammo_box_ap +---|56 # Rotary_auto_ammo_box_i +---|57 # Heavy_auto_ammo_box_k +---|58 # Heavy_auto_ammo_box_he +---|59 # Heavy_auto_ammo_box_he_frag +---|60 # Heavy_auto_ammo_box_ap +---|61 # Heavy_auto_ammo_box_i +---|62 # Battle_shell_k +---|63 # Battle_shell_he +---|64 # Battle_shell_he_frag +---|65 # Battle_shell_ap +---|66 # Battle_shell_i +---|67 # Artillery_shell_k +---|68 # Artillery_shell_he +---|69 # Artillery_shell_he_frag +---|70 # Artillery_shell_ap +---|71 # Artillery_shell_i ---@alias SLOT_NUMBER ----| '1' # Large Equipment Slot ----| '2' # Small Equipment Slot ----| '3' # Small Equipment Slot ----| '4' # Small Equipment Slot ----| '5' # Small Equipment Slot ----| '6' # Outfit Slot +---|1 # Large Equipment Slot +---|2 # Small Equipment Slot +---|3 # Small Equipment Slot +---|4 # Small Equipment Slot +---|5 # Small Equipment Slot +---|6 # Outfit Slot ---@alias FLUID_TYPE ----| '0' # Water ----| '1' # Diesel ----| '2' # Jet_fuel ----| '3' # Air ----| '4' # Exhaust ----| '5' # Oil ----| '6' # Saltwater +---|0 # Water +---|1 # Diesel +---|2 # Jet_fuel +---|3 # Air +---|4 # Exhaust +---|5 # Oil +---|6 # Saltwater ---@alias GAME_SETTING ----| '"third_person"' ----| '"third_person_vehicle"' ----| '"vehicle_damage"' ----| '"player_damage"' ----| '"npc_damage"' ----| '"sharks"' ----| '"fast_travel"' ----| '"teleport_vehicle"' ----| '"rogue_mode"' ----| '"auto_refuel"' ----| '"megalodon"' ----| '"map_show_players"' ----| '"map_show_vehicles"' ----| '"show_3d_waypoints"' ----| '"show_name_plates"' ----| '"day_night_length"' # currently cannot be written to ----| '"sunrise"' # currently cannot be written to ----| '"sunset"' # currently cannot be written to ----| '"infinite_money"' ----| '"settings_menu"' ----| '"unlock_all_islands"' ----| '"infinite_batteries"' ----| '"infinite_fuel"' ----| '"engine_overheating"' ----| '"no_clip"' ----| '"map_teleport"' ----| '"cleanup_vehicle"' ----| '"clear_fow"' # clear fog of war ----| '"vehicle_spawning"' ----| '"photo_mode"' ----| '"respawning"' ----| '"settings_menu_lock"' ----| '"despawn_on_leave"' # despawn player characters when they leave a server ----| '"unlock_all_components"' +---| "third_person" +---| "third_person_vehicle" +---| "vehicle_damage" +---| "player_damage" +---| "npc_damage" +---| "sharks" Allow sharks to spawn. +---| "fast_travel" +---| "teleport_vehicle" +---| "rogue_mode" +---| "auto_refuel" +---| "megalodon" Allow big shark to spawn. +---| "map_show_players" +---| "map_show_vehicles" +---| "show_3d_waypoints" +---| "show_name_plates" +---| "day_night_length" # currently cannot be written to +---| "sunrise" # currently cannot be written to +---| "sunset" # currently cannot be written to +---| "infinite_money" +---| "settings_menu" +---| "unlock_all_islands" # Cannot be unset once activated. +---| "infinite_batteries" +---| "infinite_fuel" +---| "engine_overheating" +---| "no_clip" +---| "map_teleport" +---| "cleanup_vehicle" +---| "clear_fow" # clear fog of war +---| "vehicle_spawning" +---| "photo_mode" +---| "respawning" +---| "settings_menu_lock" +---| "despawn_on_leave" # despawn player characters when they leave a server +---| "unlock_all_components" + + +---@alias ZONE_TYPE +---|0 Box +---|1 Sphere +---|2 Radius --#endregion --- Callback Functions -- ---#region +--#region Callback Functions ---Called every game tick ---@param game_ticks number The number of ticks that have passed this frame. Usually this value is 1 but when the player is sleeping, it is 400 function onTick(game_ticks) end ---Called when world is loaded ----@param is_new_save boolean If the world is new +---@param is_new_save boolean If the world is new (just created). function onCreate(is_new_save) end ---Called when the world is exited and the server is closed @@ -319,7 +348,7 @@ function onDestroy() end ---Called when a user enters a message prefixed by `?` in the chat ---@param message string The entire message sent by the player ----@param peer_id peer_id The peer_id of the player that sent the command +---@param peer_id Peer_ID The peer_id of the player that sent the command ---@param admin boolean If the player executing the command is an admin ---@param auth boolean If the player executing the command is authorized ---@param command string The command entered by the player **including** the `?` prefix @@ -338,7 +367,7 @@ function onDestroy() end function onCustomCommand(message, peer_id, admin, auth, command, ...) end ---Called when a message is sent to the chat ----@param peer_id peer_id The peer_id of the player that sent the message +---@param peer_id Peer_ID The peer_id of the player that sent the message ---@param sender_name string The name of the player that sent the message ---@param message string The message the player sent function onChatMessage(peer_id, sender_name, message) end @@ -346,113 +375,113 @@ function onChatMessage(peer_id, sender_name, message) end ---Called when a player joins the server ---@param steam_id string The steam id of the player. Must be stored as a string because Lua does not have the precision for such a large number ---@param name string The name of the player that joined ----@param peer_id peer_id The peer_id of the player that joined +---@param peer_id Peer_ID The peer_id of the player that joined ---@param admin boolean If the player is and admin ---@param auth boolean If the player is authorized function onPlayerJoin(steam_id, name, peer_id, admin, auth) end ---Called whenever a player sits in a seat or gets on a ladder ----@param peer_id peer_id The peer_id of the player that has sat down ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the seat is attached to +---@param peer_id Peer_ID The peer_id of the player that has sat down +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the seat is attached to ---@param seat_name string The name of the seat the player sat in function onPlayerSit(peer_id, vehicle_id, seat_name) end ---Called whenever a player gets out of a seat or gets off a ladder ----@param peer_id peer_id The peer_id of the player that has gotten out of the seat ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the seat is attached to +---@param peer_id Peer_ID The peer_id of the player that has gotten out of the seat +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the seat is attached to ---@param seat_name string The name of the seat the player got out of function onPlayerUnsit(peer_id, vehicle_id, seat_name) end ---Called whenever a character sits (or is sat) in a seat or gets on a ladder ----@param object_id object_id The object_id of the character that has sat down ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the seat is attached to +---@param object_id Object_ID The object_id of the character that has sat down +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the seat is attached to ---@param seat_name string The name of the seat the character sat in function onCharacterSit(object_id, vehicle_id, seat_name) end ---Called whenever a character gets out of it's seat (or is picked up from their seat) or gets off a ladder ----@param object_id object_id The object_id of the character that has gotten out of the seat ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the seat is attached to +---@param object_id Object_ID The object_id of the character that has gotten out of the seat +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the seat is attached to ---@param seat_name string The name of the seat the character got out of function onCharacterUnsit(object_id, vehicle_id, seat_name) end ---Called when a player **re**spawns ----@param peer_id peer_id The peer_id of the user that has respawned +---@param peer_id Peer_ID The peer_id of the user that has respawned function onPlayerRespawn(peer_id) end ---Called whenever a player leaves the server ---@param steam_id string The steam_id of the player that has left. Must be stored as a string because Lua does not have the precision for such a large number ---@param name string The name of the player that has left ----@param peer_id peer_id The peer_id of the player that has left +---@param peer_id Peer_ID The peer_id of the player that has left ---@param admin boolean If the player was an admin ---@param auth boolean If the player was authorized function onPlayerLeave(steam_id, name, peer_id, admin, auth) end ---Called whenever a player opens/closes their map ----@param peer_id peer_id The peer_id of the player +---@param peer_id Peer_ID The peer_id of the player ---@param open boolean Wether the map is now open or closed function onToggleMap(peer_id, open) end ---Called whenever a player dies ---@param steam_id string The steam_id of the player that has died ---@param name string The name of the player that has died ----@param peer_id peer_id The peer_id of the player that has died +---@param peer_id Peer_ID The peer_id of the player that has died ---@param admin boolean If the player is an admin ---@param auth boolean If the player is authorized function onPlayerDie(steam_id, name, peer_id, admin, auth) end ----Called whenever a vehicle is spawned ----@param vehicle_id vehicle_id The vehicle_id of the new vehicle ----@param peer_id peer_id The peer_id of the player that spawned the vehicle. If the vehicle was spawned by the server it will be -1 +---Called whenever a vehicle is spawned (added to the world). +---@param vehicle_id Vehicle_ID The vehicle_id of the new vehicle +---@param peer_id Peer_ID The peer_id of the player that spawned the vehicle. If the vehicle was spawned by the server it will be -1 ---@param x number The x position the vehicle was spawned at ---@param y number The y position the vehicle was spawned at ---@param z number The z position the vehicle was spawned at ---@param cost number The cost of the vehicle in Stormworks currency function onVehicleSpawn(vehicle_id, peer_id, x, y, z, cost) end ----Called whenever a vehicle is despawned ----@param vehicle_id vehicle_id The vehicle_id of the vehicle that has been despawned ----@param peer_id peer_id The peer_id of the player that despawned the vehicle. If it was despawned by the server then this value will be -1 +---Called whenever a vehicle is despawned (removed from the world). +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle that has been despawned +---@param peer_id Peer_ID The peer_id of the player that despawned the vehicle. If it was despawned by the server then this value will be -1 function onVehicleDespawn(vehicle_id, peer_id) end ----Called whenever a vehicle loads into the world ----@param vehicle_id vehicle_id The vehicle_id of the vehicle that has loaded +---Called whenever a vehicle loads into the world (is visible and has physics) +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle that has loaded function onVehicleLoad(vehicle_id) end ----Called whenever a vehicle unloads from the world ----@param vehicle_id vehicle_id The vehicle_id of the vehicle that has unloaded +---Called whenever a vehicle unloads from the world (no longer visible, no physics but still present). +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle that has unloaded function onVehicleUnload(vehicle_id) end ---Called whenever a vehicle is teleported ----@param vehicle_id vehicle_id The vehicle_id of the vehicle that has teleported ----@param peer_id peer_id The peer_id of the player that teleported the vehicle. If the server teleported it, this values is -1 +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle that has teleported +---@param peer_id Peer_ID The peer_id of the player that teleported the vehicle. If the server teleported it, this values is -1 ---@param x number The new x position value ---@param y number The new y position value ---@param z number The new z position value function onVehicleTeleport(vehicle_id, peer_id, x, y, z) end ---Called whenever an object loads and begins simulating ----@param object_id object_id The object_id of the object that has loaded +---@param object_id Object_ID The object_id of the object that has loaded function onObjectLoad(object_id) end ---Called whenever an object unloads and stops simulating ----@param object_id object_id The object_id of the object that has unloaded +---@param object_id Object_ID The object_id of the object that has unloaded function onObjectUnload(object_id) end ---Called whenever a button is pressed/released (but not held) on a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the button is a part of ----@param peer_id peer_id The peer_id of the player that pressed the button. If the button was pressed by the server this value will be -1 +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the button is a part of +---@param peer_id Peer_ID The peer_id of the player that pressed the button. If the button was pressed by the server this value will be -1 ---@param button_name string The name of the button that was pressed function onButtonPress(vehicle_id, peer_id, button_name) end ---Called whenever an addon's component is spawned ----@param id number The vehicle_id **or** object_id of the component +---@param id Vehicle_ID|Object_ID The vehicle_id **or** object_id of the component ---@param component_name string The name of the component ---@param type TYPE_STRING The component type ---@param addon_index number The index of the addon that this component belongs to function onSpawnAddonComponent(id, component_name, type, addon_index) end ---Called whenever a vehicle receives damage ----@param vehicle_id vehicle_id The vehicle_id of the damaged vehicle +---@param vehicle_id Vehicle_ID The vehicle_id of the damaged vehicle ---@param damage_amount number The amount of damage the vehicle received ---@param voxel_x number The x position of the voxel that was damaged ---@param voxel_y number The y position of the voxel that was damaged @@ -488,42 +517,41 @@ function onForestFireExtinguished(objective_id, x, y, z) end --#endregion --- UI -- ---#region +--#region UI ---Prints a message in the chat for players ---@param title string The title of message. Appears in orange on the left side of the chat like a player's name ---@param message string|number The content of the message ----@param peer_id peer_id The peer_id of the player to send the message to. -1 (default) will send to all players +---@param peer_id? Peer_ID The peer_id of the player to send the message to. -1 (default) will send to all players function server.announce(title, message, peer_id) end ---Will display a card/toast on the right side of the screen containing the title, message, and an icon based on the NOTIFICATION_TYPE ----@param peer_id peer_id The peer_id of the player to send the message to +---@param peer_id Peer_ID The peer_id of the player to send the message to ---@param title string|number The title of the message. Appears above the message ---@param message string|number The content of the message ---@param NOTIFICATION_TYPE NOTIFICATION_TYPE The type of notification. Dictates the icon displayed and color of the title function server.notify(peer_id, title, message, NOTIFICATION_TYPE) end ---Returns a unique ID to be used with all other UI functions. Removing a mapID will remove all of it's associated UI elements ----@return number ui_id The id to be used with other UI functions. UI elements that contain unique data MUST have their own ui_id. However, if you want to display the same label or popup for all players, you can re-use the same ui_id +---@return UI_ID ui_id The id to be used with other UI functions. UI elements that contain unique data MUST have their own ui_id. However, if you want to display the same label or popup for all players, you can re-use the same ui_id function server.getMapID() end ---Removes all UI that uses the provided ui_id for the provided player ----@param peer_id peer_id The peer_id of the player who's UI you want to change ----@param ui_id number The ui_id of the UI elements you want to remove +---@param peer_id Peer_ID The peer_id of the player who's UI you want to change +---@param ui_id UI_ID The ui_id of the UI elements you want to remove function server.removeMapID(peer_id, ui_id) end ---Add a map marker for the specified player ----@param peer_id peer_id The peer_id of the player you want to add a map object for ----@param ui_id number The ui_id you want to use for this object +---@param peer_id Peer_ID The peer_id of the player you want to add a map object for +---@param ui_id UI_ID The ui_id you want to use for this object ---@param position_type POSITION_TYPE The type of position this object will use. If set to 1 (vehicle) or 2 (object), the marker will track the vehicle/object using it's ID, if provided ---@param marker_type MARKER_TYPE The type of marker this object will use (visual appearance) ---@param x number The x position of the marker ---@param z number The z position of the marker ---@param parent_local_x number The x position offset value for when the marker is tracking a vehicle/object ---@param parent_local_z number The y position offset value for when the marker is tracking a vehicle/object ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to track if `position_type = 1` ----@param object_id object_id The object_id of the object to track if `position_type = 2` +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to track if `position_type = 1` +---@param object_id Object_ID The object_id of the object to track if `position_type = 2` ---@param label string The label that appears next to the object on the map ---@param radius number The radius of the marker area, not the marker icon size. ---@param hover_label string The text that appears when hovering the map marker @@ -534,13 +562,13 @@ function server.removeMapID(peer_id, ui_id) end function server.addMapObject(peer_id, ui_id, position_type, marker_type, x, z, parent_local_x, parent_local_z, vehicle_id, object_id, label, radius, hover_label, r, g, b, a) end ---Removes a map object with the specified ui_id for the specified player ----@param peer_id peer_id The peer_id of the player to remove the map object for ----@param ui_id number The ui_id of the map object to remove +---@param peer_id Peer_ID The peer_id of the player to remove the map object for +---@param ui_id UI_ID The ui_id of the map object to remove function server.removeMapObject(peer_id, ui_id) end ---Add a label to the map, like those that appear at named locations on the map. Labels will appear under the fog of war, meaning they are hidden until discovered. ----@param peer_id peer_id The peer_id of the player to add the label for ----@param ui_id number The ui_id to use for this label +---@param peer_id Peer_ID The peer_id of the player to add the label for +---@param ui_id UI_ID The ui_id to use for this label ---@param label_type LABEL_TYPE The type of icon to use for this label ---@param name string The text that should appear on this label ---@param x number The x position of this label in world space @@ -548,15 +576,15 @@ function server.removeMapObject(peer_id, ui_id) end function server.addMapLabel(peer_id, ui_id, label_type, name, x, z) end ---Remove a map label for a player ----@param peer_id peer_id The peer_id of the player you want to remove this label for ----@param ui_id number The ui_id of this label +---@param peer_id Peer_ID The peer_id of the player you want to remove this label for +---@param ui_id UI_ID The ui_id of this label function server.removeMapLabel(peer_id, ui_id) end ---Draw a line on the map for a player ----@param peer_id peer_id The peer_id of the player to draw the line for ----@param ui_id number The ui_id to use for this line ----@param start_matrix matrix The starting matrix for the line ----@param end_matrix matrix The end matrix for the line +---@param peer_id Peer_ID The peer_id of the player to draw the line for +---@param ui_id UI_ID The ui_id to use for this line +---@param start_matrix Transform The starting matrix for the line +---@param end_matrix Transform The end matrix for the line ---@param width number The width of the line ---@param r number? The red colour value of the line ---@param g number? The green colour value of the line @@ -565,13 +593,13 @@ function server.removeMapLabel(peer_id, ui_id) end function server.addMapLine(peer_id, ui_id, start_matrix, end_matrix, width, r, g, b, a) end ---Remove a line from the map for a player ----@param peer_id peer_id The peer_id of the player to remove the line for ----@param ui_id number The ui_id of this line +---@param peer_id Peer_ID The peer_id of the player to remove the line for +---@param ui_id UI_ID The ui_id of this line function server.removeMapLine(peer_id, ui_id) end ---Creates or overwrites an in-world popup for the player ----@param peer_id peer_id The peer_id of the player to set the popup for ----@param ui_id number The ui_id to use for this popup +---@param peer_id Peer_ID The peer_id of the player to set the popup for +---@param ui_id UI_ID The ui_id to use for this popup ---@param name string The name of the popup. Seems to have no effect ---@param is_shown boolean If the popup is visible or not ---@param text string The text that should appear within the popup @@ -579,13 +607,13 @@ function server.removeMapLine(peer_id, ui_id) end ---@param y number The y position value of the popup in world space. Serves as a relative offset if a parent vehicle/object is provided ---@param z number The z position value of the popup in world space. Serves as a relative offset if a parent vehicle/object is provided ---@param render_distance number The distance in meters that the player should be able to see the popup. If the user is father than this amount away, the popup will no longer show. Setting this value to 0 means it will always be visible ----@param parent_vehicle_id? vehicle_id The vehicle_id of the parent vehicle to follow. Optional ----@param parent_object_id? object_id The object_id of the parent object to follow. Optional +---@param parent_vehicle_id? Vehicle_ID The vehicle_id of the parent vehicle to follow. Optional +---@param parent_object_id? Object_ID The object_id of the parent object to follow. Optional function server.setPopup(peer_id, ui_id, name, is_shown, text, x, y, z, render_distance, parent_vehicle_id, parent_object_id) end ---Creates or overwrites an on-screen popup for the player, like a UI element ----@param peer_id peer_id The peer_id of the player to set the popup for ----@param ui_id number The ui_id to use for this popup +---@param peer_id Peer_ID The peer_id of the player to set the popup for +---@param ui_id UI_ID The ui_id to use for this popup ---@param name string The name of the popup. Seems to have no effect ---@param is_shown boolean If the popup is visible or not ---@param text string The text that should appear within the popup @@ -594,37 +622,36 @@ function server.setPopup(peer_id, ui_id, name, is_shown, text, x, y, z, render_d function server.setPopupScreen(peer_id, ui_id, name, is_shown, text, horizontal_offset, vertical_offset) end ---Remove a popup for a player. Works on screen popups as well ----@param peer_id peer_id The peer_id of the player to remove the popup for ----@param ui_id number The ui_id of the popup +---@param peer_id Peer_ID The peer_id of the player to remove the popup for +---@param ui_id UI_ID The ui_id of the popup function server.removePopup(peer_id, ui_id) end --#endregion --- Administrative -- ---#region +--#region Administrative ---Bans a player from this save. Cannot be reversed unless you create a new save. ----@param peer_id peer_id The peer_id of the player to ban +---@param peer_id Peer_ID The peer_id of the player to ban function server.banPlayer(peer_id) end ---Kicks a player from the server ----@param peer_id peer_id The peer_id of the player to kick +---@param peer_id Peer_ID The peer_id of the player to kick function server.kickPlayer(peer_id) end ---Makes the target player an admin. Gives them access to admin commands such as ?kick and ?ban ----@param peer_id peer_id The peer_id of the player to give admin permissions to +---@param peer_id Peer_ID The peer_id of the player to give admin permissions to function server.addAdmin(peer_id) end ---Removes admin privileges from a player ----@param peer_id peer_id The peer_id of the player to remove admin permissions from +---@param peer_id Peer_ID The peer_id of the player to remove admin permissions from function server.removeAdmin(peer_id) end ---Authorizes the target player. Gives them access to spawn vehicles ----@param peer_id peer_id The peer_id of the player to authorize +---@param peer_id Peer_ID The peer_id of the player to authorize function server.addAuth(peer_id) end ---Removes auth permissions from a player. This disables their ability to spawn vehicles ----@param peer_id peer_id The peer_id of the player to remove authorization from +---@param peer_id Peer_ID The peer_id of the player to remove authorization from function server.removeAuth(peer_id) end ---Saves the game. Only works on dedicated servers @@ -633,8 +660,7 @@ function server.save(save_name) end --#endregion --- Addon -- ---#region +--#region Addon ---Get the index of this addon ---@param name? string The name of the index you want to get the index of. If omitted, this function will get the current addon's index @@ -655,10 +681,10 @@ function server.getLocationIndex(addon_index, name) return location_index, is_su function server.spawnThisAddonLocation(name) return is_success end ---Spawns an addon location using indexes ----@param matrix matrix The matrix to spawn the location at. If the x, y, z of the matrix are 0, 0, 0, the location will be spawned at a random position in a random tile of the same type as this location's +---@param matrix Transform The matrix to spawn the location at. If the x, y, z of the matrix are 0, 0, 0, the location will be spawned at a random position in a random tile of the same type as this location's ---@param addon_index number The index of the addon this location belongs to ---@param location_index number The index of the location ----@return matrix matrix The spawn location matrix +---@return Transform matrix The spawn location matrix ---@return boolean is_success If the function succeeded function server.spawnAddonLocation(matrix, addon_index, location_index) return matrix, is_success end @@ -669,9 +695,15 @@ function server.spawnAddonLocation(matrix, addon_index, location_index) return m ---@return boolean is_success If the function succeeded function server.getAddonPath(name, is_rom) return path, is_success end + +---@class GetZonesResult : GetAddonDataResult +---@field size { x: number, y: number, z: number } The size of the zone. +---@field radius number The radius of the zone if the type is sphere. +---@field type ZONE_TYPE The type (shape) of the zone. + ---Get a table of all active ENV mod zones. You can provide tags seperated by commas to only return zones with matching tags. ---@vararg string Tags you want to use to filter ----@return table zone_list The table of active zones +---@return GetZonesResult[] zone_list The table of active zones ---## Example zone_list: ---``` ---{ @@ -686,14 +718,10 @@ function server.getAddonPath(name, is_rom) return path, is_success end --- } ---} ---``` ---- ## Zone types: ----0. box ----1. sphere ----2. radius function server.getZones(...) return zone_list end ---Check if a matrix is in an ENV mod zone ----@param matrix matrix The matrix to check +---@param matrix Transform The matrix to check ---@param zone_display_name string The name of the ENV mod zone to check against ---@return boolean in_zone If the matrix is within the zone ---@return boolean is_success If the function succeeded @@ -703,9 +731,15 @@ function server.isInZone(matrix, zone_display_name) return in_zone, is_success e ---@return number count The number of addons currently active function server.getAddonCount() return count end +---@class GetAddonDataResult +---@field name string Name of the addon. +---@field path_id string Path of the addon. +---@field file_store boolean is_app_data +---@field location_count integer The number of locations in the addon. + ---Get an addon's data ---@param addon_index number The index of the addon ----@return table? addon_data The data of the addon +---@return GetAddonDataResult? addon_data The data of the addon --- ## Example Addon Data ---``` ---{ @@ -717,10 +751,17 @@ function server.getAddonCount() return count end ---``` function server.getAddonData(addon_index) return addon_data end +---@class GetLocationDataResult +---@field name string Name of the location. +---@field tile string Name of the Tile the location is on. +---@field env_spawn_count? integer How many times the location will spawn, 0 for unlimited. This only matters if the location is on a non-unique tile. +---@field env_mod boolean Is this location an env_mod (env_mods are always active, mission_locations must be spawned by script). +---@field component_count integer The number of components in the location. + ---Get data on a location ---@param addon_index number The index of the addon that the location is a part of ---@param location_index number The index of the location ----@return table? location_data The data on the location +---@return GetLocationDataResult location_data The data on the location ---@return boolean is_success If the function succeeded ---## Example Location Data ---``` @@ -738,7 +779,7 @@ function server.getLocationData(addon_index, location_index) return location_dat ---@param addon_index number The index of the addon that the location is a part of ---@param location_index number The index of the location that the component is a part of ---@param component_index number The index of the component ----@return table component_data The data on the component +---@return GetLocationComponentDataResult component_data The data on the component ---@return boolean is_success If the function succeeded ---## Example Component Data ---``` @@ -756,13 +797,28 @@ function server.getLocationData(addon_index, location_index) return location_dat ---``` function server.getLocationComponentData(addon_index, location_index, component_index) return component_data, is_success end +---@class AddonComponentBase +---@field tags_full string +---@field tags string[] +---@field display_name string +---@field type TYPE_STRING +---@field transform Transform +---@field id Object_ID|Vehicle_ID + +---@class GetLocationComponentDataResult : AddonComponentBase +---@field dynamic_object_type OBJECT_TYPE +---@field vehicle_parent_component_id Vehicle_ID +---@field character_outfit_type OUTFIT_TYPE + +---@class SpawnAddonComponentResult : AddonComponentBase + ---Spawn an addon's component ----@param matrix matrix The matrix to spawn the component at +---@param matrix Transform The matrix to spawn the component at ---@param addon_index number The index of the addon that the location is a part of ---@param location_index number The index of the location that the component is a part of ---@param component_index number The index of the component ----@param parent_vehicle_id? vehicle_id The parent vehicle for a zone or fire ----@return table component Data on the component +---@param parent_vehicle_id? Vehicle_ID The parent vehicle for a zone or fire +---@return SpawnAddonComponentResult component Data on the component ---@return boolean is_success If the function succeeded ---## Example Component Data ---``` @@ -778,21 +834,27 @@ function server.getLocationComponentData(addon_index, location_index, component_ function server.spawnAddonComponent(matrix, addon_index, location_index, component_index, parent_vehicle_id) return component, is_success end ---Spawn a vehicle from an addon ----@param matrix matrix The matrix position to spawn the vehicle at +---@param matrix Transform The matrix position to spawn the vehicle at ---@param addon_index number The index of the addon that the vehicle belongs to ---@param component_id number The id of the component ----@return vehicle_id|nil vehicle_id The vehicle_id of the vehicle that has spawned +---@return Vehicle_ID|nil vehicle_id The vehicle_id of the vehicle that has spawned ---@return boolean is_success If the function succeeded ---@see `server.getLocationComponentData()` to get component_id function server.spawnAddonVehicle(matrix, addon_index, component_id) return vehicle_id, is_success end --#endregion --- Player -- ---#region +--#region Player + +---@class PlayerListItem +---@field id Peer_ID +---@field name string +---@field admin boolean +---@field auth boolean +---@field steam_id Steam_ID ---Get a table that lists data on all players connected to the server ----@return table player_list The table of player data +---@return PlayerListItem[] player_list The table of player data ---## Example Player Data ---``` --- { @@ -809,97 +871,107 @@ function server.spawnAddonVehicle(matrix, addon_index, component_id) return vehi function server.getPlayers() return player_list end ---Get the name of the player as it appears to the server ----@param peer_id peer_id The peer_id of the player who's name will be found +---@param peer_id Peer_ID The peer_id of the player who's name will be found ---@return string name The name of the player ---@return boolean is_success If the function succeeded and a name was returned function server.getPlayerName(peer_id) return name, is_success end ---Get the position of a player. This matrix does not contain any rotation data ----@param peer_id peer_id The peer_id of the player to get the position of ----@return matrix player_matrix The position of the player as a matrix +---@param peer_id Peer_ID The peer_id of the player to get the position of +---@return Transform player_matrix The position of the player as a matrix ---@return boolean is_success If the function succeeded function server.getPlayerPos(peer_id) return player_matrix, is_success end ---Set the position of a player ----@param peer_id peer_id The peer_id of the player to teleport ----@param matrix matrix The new position of the player +---@param peer_id Peer_ID The peer_id of the player to teleport +---@param matrix Transform The new position of the player ---@return boolean is_success If the function succeeded function server.setPlayerPos(peer_id, matrix) return is_success end ---Get the player's look direction vector ----@param peer_id peer_id The peer_id of the player to get the look direction of +---@param peer_id Peer_ID The peer_id of the player to get the look direction of ---@return number x Look direction vector on the x axis ---@return number y Look direction vector on the y axis ---@return number z Look direction vector on the z axis function server.getPlayerLookDirection(peer_id) return x, y, z end ----Get the player's character_id ----@param peer_id peer_id The peer_id of the player to get the character_id of ----@return object_id object_id The object_id of the player's character +---Get the id of the character object that represents that player. +---@param peer_id Peer_ID The peer_id of the player to get the object_id of +---@return Object_ID object_id The object_id of the player's character ---@return boolean is_success If the function succeeded function server.getPlayerCharacterID(peer_id) return object_id, is_success end --#endregion --- Vehicle -- ---#region +--#region Vehicle ---Spawns a vehicle from the host's vehicle save directory ----@param matrix matrix The matrix position to spawn the vehicle at +---@param matrix Transform The matrix position to spawn the vehicle at ---@param save_name string The name of the vehicle save to spawn ----@return vehicle_id|nil vehicle_id The vehicle_id of the vehicle that has been spawned +---@return Vehicle_ID|nil vehicle_id The vehicle_id of the vehicle that has been spawned ---@return boolean is_success If the function succeeded function server.spawnVehicle(matrix, save_name) return vehicle_id, is_success end ---Despawns a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to despawn +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to despawn ---@param instant boolean If the vehicle should be despawned immediately or when it unloads from the world ---@return boolean is_success If the function succeeded function server.despawnVehicle(vehicle_id, instant) return is_success end ---Get the position of a vehicle. A specific voxel of the vehicle can be specified ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to get the position of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to get the position of ---@param voxel_x? number The x position of the voxel offset ---@param voxel_y? number The y position of the voxel offset ---@param voxel_z? number The z position of the voxel offset ----@return matrix matrix The position matrix +---@return Transform matrix The position matrix ---@return boolean is_success If the function succeeded function server.getVehiclePos(vehicle_id, voxel_x, voxel_y, voxel_z) return matrix, is_success end ---Set the position of a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to teleport ----@param matrix matrix The matrix position the vehicle should be teleported to +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to teleport +---@param matrix Transform The matrix position the vehicle should be teleported to ---@return boolean is_success If the function succeeded function server.setVehiclePos(vehicle_id, matrix) return is_success end ---Set the position of a vehicle. The target vehicle will be teleported to the target matrix and will be displaced by ---any other vehicles that are in the way. This prevents the target vehicle from being teleported inside another. ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to teleport ----@param matrix matrix The matrix position the vehicle should be teleported to +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to teleport +---@param matrix Transform The matrix position the vehicle should be teleported to ---@return boolean is_success If the function succeeded function server.setVehiclePosSafe(vehicle_id, matrix) return is_success end ---Get a vehicle's file name. If the vehicle has not been saved, the name will be "Error" ----@param vehicle_id vehicle_id The id of the vehicle +---@param vehicle_id Vehicle_ID The id of the vehicle ---@return string name The name of the vehicle ---@return boolean is_success If the function succeeded and a name was returned function server.getVehicleName(vehicle_id) return name, is_success end + +---@class VehicleData +---@field tags_full string Raw tags string +---@field tags string[] Parsed tags +---@field filename string? +---@field transform Transform +---@field simulating boolean +---@field mass number +---@field characters Object_ID[] +---@field voxels integer Voxel count + ---Get data on a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to get data on ----@return table vehicle_data The data of the vehicle +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to get data on +---@return VehicleData vehicle_data The data of the vehicle ---@return boolean is_success If the function succeeded ---## Example Vehicle Data ---``` ---{ ---- ["tags_full"] = tags, ---- ["tags"] = { [i] = tag }, ---- ["filename"] = vehicle_file_name, ---- ["transform"] = transform_matrix, ---- ["simulating"] = is_simulating, ---- ["mass"] = mass, ---- ["characters"] = { char_id, char_id... } ---- ["voxels"] = voxel_count +--- ["tags_full"] = "foo=1,bar", +--- ["tags"] = { [1] = "foo=1" [2] = "bar" }, +--- ["filename"] = "vehicle_file_name.xml", +--- ["transform"] = { [1] = 1, [2] = 0, ...}, +--- ["simulating"] = true, +--- ["mass"] = 1234.125, +--- ["characters"] = { 42, 69, ... } +--- ["voxels"] = 512 ---} ---``` function server.getVehicleData(vehicle_id) return vehicle_data, is_success end @@ -908,7 +980,7 @@ function server.getVehicleData(vehicle_id) return vehicle_data, is_success end function server.cleanVehicles() end ---Override the controls of a seat on a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the seat is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the seat is a part of ---@param seat_name string The name of the seat to control. The first seat found with a matching name is selected ---@param w_axis number The w/s axis value from -1 to 1 ---@param d_axis number The a/d axis value from -1 to 1 @@ -924,14 +996,14 @@ function server.cleanVehicles() end function server.setVehicleSeat(vehicle_id, seat_name, w_axis, d_axis, up_axis, right_axis, button_1, button_2, button_3, button_4, button_5, button_6, trigger) end ---Presses the first button found a the specified vehicle with a matching name ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the button is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the button is a part of ---@param button_name string The name of the button to press function server.pressVehicleButton(vehicle_id, button_name) end ---Get the state of a button on a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the button is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the button is a part of ---@param button_name string The name of the button to get the state of ----@return table button_data The data of the button +---@return { on: boolean } button_data The data of the button ---@return boolean is_success If the function succeeded ---## Example Button Data ---``` @@ -942,9 +1014,9 @@ function server.pressVehicleButton(vehicle_id, button_name) end function server.getVehicleButton(vehicle_id, button_name) return button_data, is_success end ---Get the position of a sign block on a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the sign is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the sign is a part of ---@param sign_name string The name of the sign ----@return table sign_data The position of the sign as a table +---@return { pos: { x: integer, y: integer, z: integer }} sign_data The position of the sign as a table ---@return boolean is_success If the function succeeded ---## Example Sign Data ---``` @@ -959,15 +1031,19 @@ function server.getVehicleButton(vehicle_id, button_name) return button_data, is function server.getVehicleSign(vehicle_id, sign_name) return sign_data, is_success end ---Set the value of a vehicle's keypad ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the keypad is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the keypad is a part of ---@param keypad_name string The name of the keypad ---@param value number The value to set the keypad to function server.setVehicleKeypad(vehicle_id, keypad_name, value) end +---@class GetVehicleDialResult +---@field value number the value displayed on the dial. +---@field value2? number If the dial can display two values: that second value. + ---Get the number value from a vehicle's dial ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the dial is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the dial is a part of ---@param dial_name string The name of the dial ----@return table dial_data The data of the dial +---@return GetVehicleDialResult dial_data The data of the dial ---@return boolean is_success If the function succeeded ---## Example Dial Data ---``` @@ -979,16 +1055,21 @@ function server.setVehicleKeypad(vehicle_id, keypad_name, value) end function server.getVehicleDial(vehicle_id, dial_name) return dial_data, is_success end ---Sets the fluid type and fill level of a fluid tank on a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the tank is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the tank is a part of ---@param tank_name string The name of the tank to edit ---@param amount number The fill amount (0 - 1) ---@param fluid_type FLUID_TYPE The type of fluid to fill with function server.setVehicleTank(vehicle_id, tank_name, amount, fluid_type) end +---@class GetVehicleTankResult +---@field value number Amount of fluid in the tank. +---@field capacity number Amount of fluid the tank can contain. +---@field fluid_type FLUID_TYPE The fluid in the tank. + ---Get data on a vehicle's fluid tank ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the tank is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the tank is a part of ---@param tank_name string The name of the tank ----@return table tank_data The data of the tank +---@return GetVehicleTankResult tank_data The data of the tank ---@return boolean is_success If the function succeeded ---## Example Tank Data ---``` @@ -1001,15 +1082,15 @@ function server.setVehicleTank(vehicle_id, tank_name, amount, fluid_type) end function server.getVehicleTank(vehicle_id, tank_name) return tank_data, is_success end ---Sets the amount of coal in a hopper ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the hopper is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the hopper is a part of ---@param hopper_name string The name of the hopper ---@param amount number The amount of coal to set in the hopper function server.setVehicleHopper(vehicle_id, hopper_name, amount) end ---Get data on a vehicle's hopper ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the hopper is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the hopper is a part of ---@param hopper_name string The name of the hopper ----@return table hopper_data The data of the hopper +---@return { value: number, capacity: number } hopper_data The data of the hopper ---@return boolean is_success If the function succeeded ---## Example Hopper Data ---``` @@ -1021,15 +1102,15 @@ function server.setVehicleHopper(vehicle_id, hopper_name, amount) end function server.getVehicleHopper(vehicle_id, hopper_name) return hopper_data, is_success end ---Sets the charge of a battery ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the battery is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the battery is a part of ---@param battery_name string The name of the battery ---@param amount number The new charge of the battery function server.setVehicleBattery(vehicle_id, battery_name, amount) end ---Get data on a vehicle's battery ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the battery is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the battery is a part of ---@param battery_name string The name of the battery ----@return table battery_data The data of the battery +---@return { charge: number } battery_data The data of the battery ---@return boolean is_success If the function succeeded ---## Example Battery Data ---``` @@ -1040,15 +1121,15 @@ function server.setVehicleBattery(vehicle_id, battery_name, amount) end function server.getVehicleBattery(vehicle_id, battery_name) return battery_data, is_success end ---Set the ammo in a vehicle's weapon ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the weapon is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the weapon is a part of ---@param weapon_name string The name of the weapon ---@param amount number The ammo amount function server.setVehicleWeapon(vehicle_id, weapon_name, amount) end ---Get data on a vehicle's weapon ----@param vehicle_id vehicle_id The vehicle_id of the vehicle the weapon is a part of +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle the weapon is a part of ---@param weapon_name string The name of the weapon ----@return table weapon_data The data of the weapon +---@return { ammo: integer, capacity: integer } weapon_data The data of the weapon ---@return boolean is_success If the function succeeded ---## Example Weapon Data ---``` @@ -1060,19 +1141,19 @@ function server.setVehicleWeapon(vehicle_id, weapon_name, amount) end function server.getVehicleWeapon(vehicle_id, weapon_name) return weapon_data, is_success end ---Get the number of burning surfaces on a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to check for fires +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to check for fires ---@return number surface_count The number of surfaces that are on fire ---@return boolean is_success If the function succeeded function server.getVehicleFireCount(vehicle_id) return surface_count, is_success end ---Set the text of the default tooltip that appears when looking at a vehicle. Blocks with their own tooltip, like buttons, will override this tooltip ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to edit +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to edit ---@param text string The text to display on the tooltip ---@return boolean is_success If the function succeeded function server.setVehicleTooltip(vehicle_id, text) return is_success end ---Adds damage to a vehicle ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to damage +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to damage ---@param amount number The amount of damage to add (0 - 100) ---@param voxel_x number The x position for the center of the damage ---@param voxel_y number The y position for the center of the damage @@ -1081,7 +1162,7 @@ function server.setVehicleTooltip(vehicle_id, text) return is_success end function server.addDamage(vehicle_id, amount, voxel_x, voxel_y, voxel_z) return is_success end ---Get wether a vehicle is simulating ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to check +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to check ---@return boolean is_simulating If the vehicle is being simulated ---@return boolean is_success If the function succeeded function server.getVehicleSimulating(vehicle_id) return is_simulating, is_success end @@ -1093,131 +1174,137 @@ function server.getVehicleSimulating(vehicle_id) return is_simulating, is_succes function server.getVehicleLocal(vehicle_id) return is_local, is_success end ---Set the state of this vehicle's global transponder. All vehicles have a "transponder" that can be seen even if the vehicle is not loaded ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to set the transponder state for +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to set the transponder state for ---@param is_active boolean The state of the transponder ---@return boolean is_success If the function succeeded function server.setVehicleTransponder(vehicle_id, is_active) return is_success end ---Set a vehicle to be editable by players ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to edit +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to edit ---@param is_editable boolean If the vehicle can be edited or not ---@return boolean is_success If the function succeeded function server.setVehicleEditable(vehicle_id, is_editable) return is_success end ---Sets whether a specific vehicle should be visible on the player's map ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to edit +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to edit ---@param show_on_map boolean If the vehicle's icon is shown on the map ---@return boolean is_success If the function succeeded function server.setVehicleShownOnMap(vehicle_id, show_on_map) return is_success end ---Sets a vehicle to be invulnerable to damage. Makes a vehicle unbreakable. ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to edit +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to edit ---@param is_invulnerable boolean If the vehicle is invulnerable ---@return boolean is_success If the function succeeded function server.setVehicleInvulnerable(vehicle_id, is_invulnerable) return is_success end ---Undocumented by the devs. It can be assumed that this function resets a vehicle to the state it was in when it was spawned. ----@param vehicle_id vehicle_id The vehicle_id of the vehicle to reset +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle to reset function server.resetVehicleState(vehicle_id) end --#endregion --- Object -- ---#region +--#region Object ---Spawn an object at the specified matrix position ----@param matrix matrix The matrix to spawn the object at +---@param matrix Transform The matrix to spawn the object at ---@param object_type OBJECT_TYPE The type of object to spawn ----@return object_id object_id The object_id of the newly spawned object +---@return Object_ID object_id The object_id of the newly spawned object ---@return boolean is_success If the function succeeded function server.spawnObject(matrix, object_type) return object_id, is_success end ---Spawns a fire and/or explosion at the specified matrix position ----@param matrix matrix The matrix position to spawn the fire at +---@param matrix Transform The matrix position to spawn the fire at ---@param size number The size of the fire to spawn ---@param magnitude number The ferocity of the fire. The higher the number, the harder it is to extinguish ---@param is_lit boolean If the fire is currently lit or not ---@param is_explosive boolean If the fire should explode upon ignition ----@param parent_vehicle_id vehicle_id The vehicle_id of the parent vehicle. If set to 0 the fire will not follow a vehicle +---@param parent_vehicle_id Vehicle_ID The vehicle_id of the parent vehicle. If set to 0 the fire will not follow a vehicle ---@param explosion_magnitude number The magnitude of the explosion ----@return object_id object_id The object_id of the fire object +---@return Object_ID object_id The object_id of the fire object ---@return boolean is_success If the function succeeded function server.spawnFire(matrix, size, magnitude, is_lit, is_explosive, parent_vehicle_id, explosion_magnitude) return object_id, is_success end ---Spawns an explosion at the specified position ---**Requires Weapons DLC to be enabled** ----@param matrix matrix The matrix to spawn the explosion at +---@param matrix Transform The matrix to spawn the explosion at ---@param magnitude number The size of the explosion function server.spawnExplosion(matrix, magnitude) end ---Spawn a character object ----@param matrix matrix The matrix position to spawn the character at +---@param matrix Transform The matrix position to spawn the character at ---@param outfit_type OUTFIT_TYPE The type of outfit the character should be wearing ----@return object_id object_id The object_id of the character +---@return Object_ID object_id The object_id of the character ---@return boolean is_success If the function succeeded function server.spawnCharacter(matrix, outfit_type) return object_id, is_success end ---Spawns an animal object ----@param matrix matrix The matrix to spawn the animal at +---@param matrix Transform The matrix to spawn the animal at ---@param animal_type number The type of animal to spawn ---@param size number The size multiplier to apply to the animal ----@return object_id object_id The object_id of the animal that spawned +---@return Object_ID object_id The object_id of the animal that spawned ---@return boolean is_success If the function succeeded function server.spawnAnimal(matrix, animal_type, size) return object_id, is_success end ---Despawn an object ----@param object_id object_id The object_id of the object you want to despawn +---@param object_id Object_ID The object_id of the object you want to despawn ---@param instant boolean If the object should be despawned immediately or when it unloads from the world ---@return boolean is_success If the function succeeded function server.despawnObject(object_id, instant) return is_success end ---Get the position of an object ----@param object_id object_id The object_id of the object to get the position of ----@return matrix matrix The matrix of the object, includes rotation data +---@param object_id Object_ID The object_id of the object to get the position of +---@return Transform matrix The matrix of the object, includes rotation data ---@return boolean is_success If the function succeeded function server.getObjectPos(object_id) return matrix, is_success end ---Get wether an object is being simulated ----@param object_id object_id The object_id of the object to check +---@param object_id Object_ID The object_id of the object to check ---@return boolean is_simulating If the object is being simulated ---@return boolean is_success If the function succeeded function server.getObjectSimulating(object_id) return is_simulating, is_success end ---Set the position of an object ----@param object_id object_id The object_id of the object to set the position of ----@param matrix matrix The matrix to apply to the object, includes rotation data +---@param object_id Object_ID The object_id of the object to set the position of +---@param matrix Transform The matrix to apply to the object, includes rotation data ---@return boolean is_success If the function succeeded function server.setObjectPos(object_id, matrix) return is_success end ---Set data for a fire object ----@param object_id object_id The object_id of the fire object to edit +---@param object_id Object_ID The object_id of the fire object to edit ---@param is_lit boolean If the fire is ignited ---@param is_explosive boolean If the fire is explosive function server.setFireData(object_id, is_lit, is_explosive) end ---Get data of a fire object ----@param object_id object_id The object_id of the fire object to get data on +---@param object_id Object_ID The object_id of the fire object to get data on ---@return boolean is_lit If the fire is ignited or not ---@return boolean is_success If the function succeeded function server.getFireData(object_id) return is_lit, is_success end ---Kills the target character ----@param object_id object_id The object_id of the character to kill +---@param object_id Object_ID The object_id of the character to kill function server.killCharacter(object_id) end ---Revive the target character ----@param object_id object_id The object_id of the character to revive +---@param object_id Object_ID The object_id of the character to revive function server.reviveCharacter(object_id) end ---Teleport a character object to a seat on a vehicle ----@param object_id object_id The object_id of the character to teleport ----@param vehicle_id vehicle_id The vehicle_id of the vehicle that the seat is a part of +---@param object_id Object_ID The object_id of the character to teleport +---@param vehicle_id Vehicle_ID The vehicle_id of the vehicle that the seat is a part of ---@param seat_name? string The name of the seat to teleport the character to. If an empty string, the character will be teleported to the first seat found. function server.setCharacterSeated(object_id, vehicle_id, seat_name) end +---@class GetCharacterDataResult +---@field hp number health points. +---@field incapacitated boolean Is the character incapacitated (seriously injured and unable to move). +---@field dead boolean Is the character dead. +---@field ai boolean Is the character controlled by AI. +---@field name string + ---Get data on a character object ----@param object_id object_id The object_id of the target character ----@return table character_data The data of this character +---@param object_id Object_ID The object_id of the target character +---@return GetCharacterDataResult character_data The data of this character ---## Example Character Data ---``` ---{ @@ -1232,29 +1319,30 @@ function server.setCharacterSeated(object_id, vehicle_id, seat_name) end function server.getCharacterData(object_id) return character_data end ---Get the vehicle_id that a character is sitting in ----@param object_id object_id The object_id of the target character ----@return vehicle_id vehicle_id The vehicle_id of the vehicle the character is in +---@param object_id Object_ID The object_id of the target character +---@return Vehicle_ID vehicle_id The vehicle_id of the vehicle the character is in ---@return boolean is_success If the function succeeded function server.getCharacterVehicle(object_id) return vehicle_id, is_success end ---Sets data values for a character ----@param object_id object_id The object_id of the character +---@param object_id Object_ID The object_id of the character ---@param hp number The health of the character ---@param is_interactable boolean If the character can be interacted with (carried, asked to follow) ---@param is_ai boolean If the character has ai enabled function server.setCharacterData(object_id, hp, is_interactable, is_ai) end ---Sets what item is in a given slot for a character ----@param object_id object_id The object_id of the character to equip +---@param object_id Object_ID The object_id of the character to equip ---@param slot SLOT_NUMBER The slot to put the item in ---@param equipment_id EQUIPMENT_ID The equipment_id of the equipment to put in the slot ---@param is_active boolean If the equipment is active, such as if a transponder is currently on ---@param meta1? number The first metadata value for the item. Defaults to 0 ---@param meta2? number The second metadata value for the item. Defaults to 0 +---@return boolean is_success function server.setCharacterItem(object_id, slot, equipment_id, is_active, meta1, meta2) end ---Get the id of the object in the specified slot of a character ----@param object_id object_id The object_id of the character whose inventory will be checked +---@param object_id Object_ID The object_id of the character whose inventory will be checked ---@param slot SLOT_NUMBER The slot to check ---@return EQUIPMENT_ID equipment_id The id of the equipment in the slot ---@return boolean is_success If the function succeeded @@ -1262,8 +1350,7 @@ function server.getCharacterItem(object_id, slot) return equipment_id, is_succes --#endregion --- Game Data -- ---#region +--#region Game Data ---Set one of this save's game settings ---@param game_setting GAME_SETTING The name of the setting to set @@ -1272,52 +1359,15 @@ function server.setGameSetting(game_setting, value) end ---Get the game's settings ---@return table ----```lua ----GAME_SETTING | ----"third_person", ----"third_person_vehicle", ----"vehicle_damage", ----"player_damage", ----"npc_damage", ----"sharks", ----"fast_travel", ----"teleport_vehicle", ----"rogue_mode", ----"auto_refuel", ----"megalodon", ----"map_show_players", ----"map_show_vehicles", ----"show_3d_waypoints", ----"show_name_plates", ----"day_night_length", -- currently cannot be written to ----"sunrise", -- currently cannot be written to ----"sunset", -- currently cannot be written to ----"infinite_money", ----"settings_menu", ----"unlock_all_islands", ----"infinite_batteries", ----"infinite_fuel", ----"engine_overheating", ----"no_clip", ----"map_teleport", ----"cleanup_veicle", ----"clear_fow", -- clear fog of war ----"vehicle_spawning", ----"photo_mode", ----"respawning", ----"settings_menu_lock", ----"despawn_on_leave", -- despawn player characters when they leave a server ----"unlock_all_components" ----``` function server.getGameSettings() return game_settings end ---Set the server's money and research points amounts ----@param money number The new amount of money +---@param money currency The new amount of money ---@param research_points number The new amount of research points function server.setCurrency(money, research_points) end ---Get the amount of money the server has ----@return any +---@return currency function server.getCurrency() return money end ---Get the number of research points the server has @@ -1334,22 +1384,35 @@ function server.getDateValue() return days_survived end ---@return number year The current year function server.getDate() return day, month, year end +---@class GetTimeResult +---@field hour number Clock hour [0..23] +---@field minute number Clock minute [0..59] +---@field daylight_factor number Fraction of light intensity [0..1] where 0 night, 1 midday. +---@field percent number Fraction of progress through the day [0..1] + ---Get the current in-game time ----@return table time The current time in-game +---@return GetTimeResult time The current time in-game ---## Example Time Data ---``` ---{ ---- ["hour"] = 24_hour, ---- ["minute"] = 60_minute, ---- ["daylight_factor"] = midday (0 - 1), ---- ["percent"] = day_cycle_percent (0 - 1) +--- ["hour"] = 13, +--- ["minute"] = 55, +--- ["daylight_factor"] = 0.99, +--- ["percent"] = 0.55 ---} ---``` function server.getTime() return time end + + +---@class GetWeatherResult +---@field fog fraction +---@field rain fraction +---@field snow fraction + ---Get the current weather at the specified location ----@param matrix matrix The location to get the weather at ----@return table weather The weather at the location +---@param matrix Transform The location to get the weather at +---@return GetWeatherResult weather The weather at the location ---## Example Weather Data ---``` ---{ @@ -1362,43 +1425,55 @@ function server.getWeather(matrix) return weather end --#endregion --- Tiles -- ---#region +--#region Tiles ---Get the location of a random ocean tile within a certain range ----@param matrix matrix The start position to perform the search from +---@param matrix Transform The start position to perform the search from ---@param min_search_range number The minimum number of meters to search in ---@param max_search_range number the maximum number of meters to search in ----@return matrix|nil matrix The position matrix of the tile +---@return Transform|nil matrix The position matrix of the tile ---@return boolean is_success If the function succeeded function server.getOceanTransform(matrix, min_search_range, max_search_range) return matrix, is_success end ---Get the location of a random tile with a matching name within the search radius ----@param matrix matrix The position matrix to perform the search from +---@param matrix Transform The position matrix to perform the search from ---@param tile_name string The name of the tile to search for ---@param search_radius? number The radius to search in, in meters. Defaults to 50000m ----@return matrix|nil matrix The position matrix of the tile +---@return Transform|nil matrix The position matrix of the tile ---@return boolean is_success If the function succeeded function server.getTileTransform(matrix, tile_name, search_radius) return matrix, is_success end + +---@class GetTileResult +---@field name string +---@field sea_floor number Sea floor height relative to sea level. +---@field cost? currency Purchase cost of the tile +---@field purchased boolean + ---Get data on the tile at the specified location ----@param matrix matrix The location of the tile to get info on ----@return table tile_data The data on the tile +---@param matrix Transform The location of the tile to get info on +---@return GetTileResult tile_data The data on the tile ---@return boolean is_success If the function succeeded ---## Example Tile Data ---``` ---{ ---- ["name"] = tile_name, ---- ["sea_floor"] = sea_floor_height, ---- ["cost"] = purchase_cost, ---- ["purchased"] = is_purchased +--- ["name"] = "tile_name", +--- ["sea_floor"] = -50, +--- ["cost"] = 50000, +--- ["purchased"] = false ---} ---``` function server.getTile(matrix) return tile_data, is_success end +---@class GetStartTileResult +---@field name string +---@field x number Position X +---@field y number Position Y +---@field z number Position Z + ---Get data on the starting tile. ---Has the alias `getStartIsland()` ----@return table tile_data Data on the starting tile +---@return GetStartTileResult tile_data Data on the starting tile ---## Example Tile Data ---``` ---{ @@ -1410,14 +1485,29 @@ function server.getTile(matrix) return tile_data, is_success end ---``` function server.getStartTile() return tile_data end + +---Get data on the starting tile. +---Has the alias `getStartTile()` +---@return GetStartTileResult tile_data Data on the starting tile +---## Example Tile Data +---``` +---{ +--- ["name"] = tile_name, +--- ["x"] = x_position, +--- ["y"] = y_position, +--- ["z"] = z_position +---} +---``` +function server.getStartIsland() return server.getStartTile() end + ---Get wether a tile has been purchased ----@param matrix matrix The position matrix of the tile in question +---@param matrix Transform The position matrix of the tile in question ---@return boolean is_purchased If the tile has been purchased function server.getTilePurchased(matrix) return is_purchased end ---Get wether a matrix is inside a zone ----@param needle_matrix matrix The matrix to check wether it is in the zone ----@param zone_matrix matrix The zone to check against +---@param needle_matrix Transform The matrix to check wether it is in the zone +---@param zone_matrix Transform The zone to check against ---@param zone_size_x number The size of the zone on the x axis ---@param zone_size_y number The size of the zone on the y axis ---@param zone_size_z number The size of the zone on the z axis @@ -1425,15 +1515,15 @@ function server.getTilePurchased(matrix) return is_purchased end function server.isInTransformArea(needle_matrix, zone_matrix, zone_size_x, zone_size_y, zone_size_z) return is_in_area end ---Pathfind through the ocean from the start matrix to the end matrix ----@param start_matrix matrix The matrix to start from ----@param end_matrix matrix The matrix to end at ----@return table path The path to get from start to finish +---@param start_matrix Transform The matrix to start from +---@param end_matrix Transform The matrix to end at +---@return {x: number, z: number}[] path The path to get from start to finish ---## Example Path Data ---``` ---{ --- [i] = { ---- x = x_pos, ---- z = z_pos +--- x = 12560, +--- z = -55 --- } ---} ---``` @@ -1441,11 +1531,10 @@ function server.pathfindOcean(start_matrix, end_matrix) return path end --#endregion --- Properties -- ---#region +--#region Properties (Settings) ---Create a checkbox UI element on the main menu and get it's state ----@param text string THe text to display along with the checkbox +---@param text string The text to display along with the checkbox ---@param default_value string The default value of the checkbox ("true" or "false") ---@return boolean checked Wether the checkbox is checked function property.checkbox(text, default_value) return checked end @@ -1461,11 +1550,10 @@ function property.slider(text, min, max, increment, default_value) return value --#endregion --- AI -- ---#region +--#region AI ---Set the AI state of a character ----@param object_id object_id The object_id of the character to set the AI state of +---@param object_id Object_ID The object_id of the character to set the AI state of ---@param ai_state number The new state of the character's AI ---___ ---## AI States @@ -1524,14 +1612,17 @@ function property.slider(text, min, max, increment, default_value) return value ---Trigger = Designate function server.setAIState(object_id, ai_state) end ----Set the target destination for the AI ----@param object_id object_id The object_id of the character to set the target for ----@param matrix matrix The destination matrix the character AI should be aiming for -function server.setAITarget(object_id, matrix) end + +---@class GetAITargetResult +---@field character? Object_ID +---@field vehicle? Vehicle_ID +---@field x number X Position (X on in-game map) +---@field y number Y Position altitude +---@field z number Z Position (Y on in-game map) ---Get a character's target data ----@param object_id object_id The object_id of the character to get the data from ----@return table target_data The character's target data +---@param object_id Object_ID The object_id of the character to get the data from +---@return GetAITargetResult target_data The character's target data ---## Example Target Data ---``` ---{ @@ -1544,29 +1635,33 @@ function server.setAITarget(object_id, matrix) end ---``` function server.getAITarget(object_id) return target_data end +---Set the target destination for the AI +---@param object_id Object_ID The object_id of the character to set the target for +---@param matrix Transform The destination matrix the character AI should be aiming for +function server.setAITarget(object_id, matrix) end + ---Set the target character for a character's AI ----@param object_id object_id The object_id of the character to set target data for ----@param target_object_id object_id The object_id of the character to target +---@param object_id Object_ID The object_id of the character to set target data for +---@param target_object_id Object_ID The object_id of the character to target function server.setAITargetCharacter(object_id, target_object_id) end ---Set the target for vehicle for a character's AI ----@param object_id object_id The object_id of the character to set target data for ----@param target_vehicle_id vehicle_id The vehicle_id of the vehicle to target +---@param object_id Object_ID The object_id of the character to set target data for +---@param target_vehicle_id Vehicle_ID The vehicle_id of the vehicle to target function server.setAITargetVehicle(object_id, target_vehicle_id) end --#endregion --- Natural Disasters -- ---#region +--#region Natural Disasters ----Spawns a tsunami with it's epicenter at `matrix`. Only one tsunami/whirlpool can be active at once. A stronger event overrides a weaker one. ----@param matrix matrix The target position for the epicenter of the tsunami +---Spawns a tsunami with it's epicenter at `matrix`. Only one event (tsunami/whirlpool) can be active at once. A stronger event overrides a weaker one. +---@param matrix Transform The target position for the epicenter of the tsunami ---@param magnitude number The intensity of the tsunami ---@return boolean is_success If the tsunami was successfully started function server.spawnTsunami(matrix, magnitude) return is_success end ----Spawns a whirpool at the target position. If the ocean is too shallow at the target location, spawning will fail. Only one tsunami/whirlpool can be active at once. A stronger event overrides a weaker one. ----@param matrix matrix The target position for the epicenter of the whirlpool +---Spawns a whirpool at the target position. If the ocean is too shallow at the target location, spawning will fail. Only one event (tsunami/whirlpool) can be active at once. A stronger event overrides a weaker one. +---@param matrix Transform The target position for the epicenter of the whirlpool ---@param magnitude number The intensity of the whirlpool ---@return boolean is_success If the whirlpool was successfully started function server.spawnWhirlpool(matrix, magnitude) return is_success end @@ -1575,108 +1670,114 @@ function server.spawnWhirlpool(matrix, magnitude) return is_success end function server.cancelGerstner() end ---Spawns a tornado at the target position ----@param matrix matrix The target spawn location for the tornado +---@param matrix Transform The target spawn location for the tornado ---@return boolean is_success If the tornado was successfully started function server.spawnTornado(matrix) return is_success end ---Spawns a meteor that strikes the target position ----@param matrix matrix The target location for the meteor +---@param matrix Transform The target location for the meteor ---@param magnitude number The size of the meteor. Accepts values 0 - 1. Scales at a factor of `magnitude` * 20 ---@param spawns_tsunami boolean If the meteor should spawn a tsunami upon impact ---@return boolean is_success If the meteor was successfully spawned function server.spawnMeteor(matrix, magnitude, spawns_tsunami) return is_success end ---Spawns a meteor shower that aims for the target position ----@param matrix matrix The target location for the shower +---@param matrix Transform The target location for the shower ---@param magnitude number The size of the main meteor. Larger values also increase the number of secondary meteors. Accepts values 0 - 1. ---@param spawns_tsunami boolean If the main meteor should spawn a tsunami upon impact ---@return boolean is_success If the shower was successfully started function server.spawnMeteorShower(matrix, magnitude, spawns_tsunami) return is_success end ---Activates the closest volcano **if that tile is being simulated**. Unloaded tiles will not activate. ----@param matrix matrix The target position for starting a volcanic event. Does not need to be exact ----@return any +---@param matrix Transform The target position for starting a volcanic event. Does not need to be exact +---@return boolean is_success function server.spawnVolcano(matrix) return is_success end +---@class GetVolcanosResult +---@field x number X Position +---@field y number Y Position (height) +---@field z number Z Position (Y on the in-game map) +---@field tile_x integer Index in tile grid on x axis +---@field tile_y integer Index in tile grid in z axis + ---Gets a table of all volcanos in the world ----@return table volcanos A list of all volcanos +---@return GetVolcanosResult[] volcanos A list of all volcanos ---## Example of volcanos table: ---``` ---{ ---- x = world_x, ---- y = world_y, ---- z = world_z, ---- tile_x = tile_grid_x, ---- tile_y = tile_grid_z +--- x = 120, +--- y = 30, +--- z = -50, +--- tile_x = 22, +--- tile_y = -5 ---} ---``` function server.getVolcanos() return volcanos end --#endregion --- Matrices -- ---#region +--#region Matrices ---Multiply two matrices together ----@param matrix1 matrix The matrix to multiply ----@param matrix2 matrix The matrix to multiply by ----@return matrix multiplied_matrix The result of multiplying the matrices +---@param matrix1 Transform The matrix to multiply +---@param matrix2 Transform The matrix to multiply by +---@return Transform multiplied_matrix The result of multiplying the matrices function matrix.multiply(matrix1, matrix2) return multiplied_matrix end ---Inverts a matrix. Used in multiplication to divide. ----@param matrix matrix The matrix to invert ----@return matrix inverted_matrix The matrix, now inverted +---@param matrix Transform The matrix to invert +---@return Transform inverted_matrix The matrix, now inverted ---@see https://www.mathsisfun.com/algebra/matrix-inverse.html function matrix.invert(matrix) return inverted_matrix end ---Transposes a matrix. This flips a matrix, switching row and column indices ----@param matrix matrix The matrix to transpose ----@return matrix transposed_matrix The matrix, now transposed +---@param matrix Transform The matrix to transpose +---@return Transform transposed_matrix The matrix, now transposed ---@see https://en.wikipedia.org/wiki/Transpose function matrix.transpose(matrix) return transposed_matrix end ---Get an identity matrix ----@return matrix identity_matrix The identity matrix +---@return Transform identity_matrix The identity matrix ---@see https://en.wikipedia.org/wiki/Identity_matrix function matrix.identity() return identity_matrix end ---Get a new matrix, rotated the requested number of radians on the X axis ---@param radians number The number of radians to rotate by on the X axis ----@return matrix rotation_matrix A new matrix, rotated by the requested number of radians on the X axis +---@return Transform rotation_matrix A new matrix, rotated by the requested number of radians on the X axis function matrix.rotationX(radians) return rotation_matrix end ---Get a new matrix, rotated the requested number of radians on the Y axis ---@param radians number The number of radians to rotate by on the Y axis ----@return matrix rotation_matrix A new matrix, rotated by the requested number of radians on the Y axis +---@return Transform rotation_matrix A new matrix, rotated by the requested number of radians on the Y axis function matrix.rotationY(radians) return rotation_matrix end ---Get a new matrix, rotated the requested number of radians on the Z axis ---@param radians number The number of radians to rotate by on the Z axis ----@return matrix rotation_matrix A new matrix, rotated by the requested number of radians on the Z axis +---@return Transform rotation_matrix A new matrix, rotated by the requested number of radians on the Z axis function matrix.rotationZ(radians) return rotation_matrix end ---Returns a new matrix, translated by the specified x, y, z ---@param x number The x value to translate by. This is the same x value that is seen on the in-game map ---@param y number The y value to translate by. This is the vertical axis in the game ---@param z number The z value to translate by. This is the y axis on the map in the game ----@return matrix translated_matrix The new translated matrix +---@return Transform translated_matrix The new translated matrix function matrix.translation(x, y, z) return translated_matrix end ---Get the x, y, z position from a matrix ----@param matrix matrix The matrix to extract the position values from +---@param matrix Transform The matrix to extract the position values from ---@return number x The x position of the matrix. This is the same x value that is seen on the in-game map ---@return number y The y position of the matrix. This is the vertical axis in the game ---@return number z The z position of the matrix. This is the y axis on the map in the game function matrix.position(matrix) return x, y, z end ---Get the distance in meters between two matrices ----@param matrix1 matrix The first matrix ----@param matrix2 matrix The second matrix +---@param matrix1 Transform The first matrix +---@param matrix2 Transform The second matrix ---@return number distance The distance between the two matrices in meters function matrix.distance(matrix1, matrix2) return distance end ---Multiply a matrix by a vec4 ----@param matrix1 matrix The matrix to multiply +---@param matrix1 Transform The matrix to multiply ---@param x number The vector x value ---@param y number The vector y value ---@param z number The vector z value @@ -1691,13 +1792,12 @@ function matrix.multiplyXYZW(matrix1, x, y, z, w) return x, y, z, w end ---Returns the rotation matrix required to face the supplied vector ---@param x number The x value of the vector ---@param z number The z value of the vector ----@return matrix rotation The rotation matrix +---@return Transform rotation The rotation matrix function matrix.rotationToFaceXZ(x, z) return rotation end --#endregion --- Misc -- ---#region +--#region Misc ---Get if the tutorial is currently active ---@return boolean tutorial_completed The tutorial has been completed and is not currently running @@ -1731,4 +1831,8 @@ function server.getTimeMillisec() return time_since_load end ---@return boolean dlc_enabled If the dlc is enabled or not function server.dlcWeapons() return dlc_enabled end ---#endregion \ No newline at end of file +---Get whether or not the Arid Island DLC is enabled on this save +---@return boolean dlc_enabled If the dlc is enabled or not +function server.dlcArid() return dlc_enabled end + +--#endregion diff --git a/src/script.lua b/src/script.lua index e042d38..bc8789a 100644 --- a/src/script.lua +++ b/src/script.lua @@ -1,10 +1,12 @@ --- CARSA'S COMMANDS ----@version 2.1.0 +-- DO NOT use the in-game text editor, it WILL break the script! + ----@alias peerID number ----@alias steamID string ----@alias vehicleID number ----@alias matrix table +-- CARSA'S COMMANDS +---@version 2.1.1 +---@authors carsakiller, CrazyFluffyPony, Dargino, CodeLeopard +---@source https://github.com/carsakiller/Carsas-CommandsV2 +---@license MIT License +--- Copyright (c) 2022 carsakiller -- Used to define a owner's steamID @@ -15,18 +17,17 @@ local OWNER_STEAM_ID = "0" local DEBUG = false -local ScriptVersion = "2.0.3" -local SaveDataVersion = "2.0.3" +local ScriptVersion = "2.1.1" +local SaveDataVersion = "2.1.1" --[ LIBRARIES ]-- --#region --[ lua implementation of fzy library ]-- ---#region --- @author Seth Warn --- @source https://github.com/swarn/fzy-lua --- @license --- The MIT License (MIT) +--#region +---@author Seth Warn +---@source https://github.com/swarn/fzy-lua +---@license The MIT License (MIT) -- Copyright (c) 2020 Seth Warn @@ -62,6 +63,11 @@ local fzy = { MATCH_MAX_LENGTH = 1024 } +---String comparison +---@param needle string +---@param haystack string +---@param case_sensitive? boolean +---@return boolean function fzy.has_match(needle, haystack, case_sensitive) if not case_sensitive then needle = string.lower(needle) @@ -80,15 +86,22 @@ function fzy.has_match(needle, haystack, case_sensitive) return true end - +---is lowercase +---@param c string +---@return boolean function fzy.is_lower(c) return c:match("%l") end - +---is uppercase +---@param c string +---@return boolean function fzy.is_upper(c) return c:match("%u") end +---Compute the bonus for the search space. +---@param haystack string +---@return table function fzy.precompute_bonus(haystack) local match_bonus = {} @@ -113,6 +126,12 @@ function fzy.precompute_bonus(haystack) return match_bonus end + +---@param needle string +---@param haystack string +---@param D table +---@param M table +---@param case_sensitive? boolean function fzy.compute(needle, haystack, D, M, case_sensitive) -- Note that the match bonuses must be computed before the arguments are -- converted to lowercase, since there are bonuses for camelCase. @@ -162,6 +181,11 @@ function fzy.compute(needle, haystack, D, M, case_sensitive) end end +---Assign a score based on the match quality. +---@param needle string +---@param haystack string +---@param case_sensitive? boolean +---@return number function fzy.score(needle, haystack, case_sensitive) local n = string.len(needle) local m = string.len(haystack) @@ -192,6 +216,9 @@ json = {} -- Internal functions. +---Check what SCOOP type the object is. +---@param obj any +---@return "boolean"|"function"|"nil"|"number"|"string"|"table"|"array"|"thread"|"userdata" function kind_of(obj) if type(obj) ~= 'table' then return type(obj) end local i = 1 @@ -201,6 +228,9 @@ function kind_of(obj) if i == 1 then return 'table' else return 'array' end end +---Escape the string by prepending \ +---@param s string +---@return string function escape_str(s) local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'} local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'} @@ -214,6 +244,12 @@ end -- 1. Delimiter found: pos = pos after leading space + delim; did_find = true. -- 2. Delimiter not found: pos = pos after leading space; did_find = false. -- This throws an error if err_if_missing is true and the delim is not found. +---@param str string +---@param pos integer +---@param delim string +---@param err_if_missing? boolean +---@return integer pos +---@return boolean did_find function skip_delim(str, pos, delim, err_if_missing) pos = pos + #str:match('^%s*', pos) if str:sub(pos, pos) ~= delim then @@ -227,6 +263,12 @@ end -- Expects the given pos to be the first character after the opening quote. -- Returns val, pos; the returned pos is after the closing quote character. +---Parses the string value at the position from Json. +---@param str string Json string +---@param pos integer Position of the first character of the value to be parsed (so after the opening quote). +---@param val? string Value, in case the string was ssplit. +---@return string result +---@return integer pos The position in the Json string after reading (past the closing quote). function parse_str_val(str, pos, val) val = val or '' local early_end_error = 'End of input found while parsing string.' @@ -242,6 +284,11 @@ function parse_str_val(str, pos, val) end -- Returns val, pos; the returned pos is after the number's final character. +---parse a number from Json string. +---@param str string Full Json string. +---@param pos integer Position of the first character of the number-as-a-string. +---@return number|nil Result number or nil +---@return integer pos Position after reading. function parse_num_val(str, pos) local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos) local val = tonumber(num_str) @@ -291,6 +338,12 @@ end json.null = {} -- This is a one-off table to represent the null value. +---Parse Json string to Lua table. +---@param str string|nil +---@param pos integer|nil +---@param end_delim string? +---@return table|number|boolean|string|nil result. +---@return integer|nil position in the input string. function json.parse(str, pos, end_delim) -- safety in case someone tries to parse something that is not a string if str == nil then @@ -303,25 +356,32 @@ function json.parse(str, pos, end_delim) pos = pos or 1 if pos > #str then companionError('Reached unexpected end of input.') end - local pos = pos + #str:match('^%s*', pos) -- Skip whitespace. + pos = pos + #str:match('^%s*', pos) -- Skip whitespace. local first = str:sub(pos, pos) if first == '{' then -- Parse an object. - local obj, key, delim_found = {}, true, true + local obj = {} + ---@type any + local key = true + local delim_found = true pos = pos + 1 while true do key, pos = json.parse(str, pos, '}') + ---@cast pos integer if key == nil then return obj, pos end if not delim_found then companionError('Comma missing between object items.') end pos = skip_delim(str, pos, ':', true) -- true -> error if missing. obj[key], pos = json.parse(str, pos) + ---@cast pos integer pos, delim_found = skip_delim(str, pos, ',') end elseif first == '[' then -- Parse an array. - local arr, val, delim_found = {}, true, true + local arr = {} + local val, delim_found pos = pos + 1 while true do val, pos = json.parse(str, pos, ']') if val == nil then return arr, pos end + ---@cast pos integer if not delim_found then companionError('Comma missing between array items.') end arr[#arr + 1] = val pos, delim_found = skip_delim(str, pos, ',') @@ -336,10 +396,13 @@ function json.parse(str, pos, end_delim) local literals = {['true'] = true, ['false'] = false, ['null'] = json.null} for lit_str, lit_val in pairs(literals) do local lit_end = pos + #lit_str - 1 - if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end + if str:sub(pos, lit_end) == lit_str then + return lit_val, lit_end + 1 + end end local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10) companionError('Invalid json syntax starting at ' .. pos_info_str) + return nil, nil end end @@ -355,8 +418,12 @@ local char_to_hex = function(c) return string.format("%%%02X", string.byte(c)) end +---@param url string +---@return string function urlencode(url) if url == nil then +-- Justification: Error condition. +---@diagnostic disable-next-line: missing-return-value return end url = url:gsub("\n", "\r\n") @@ -379,6 +446,12 @@ urldecode = function(url) end --#endregion +-- extend math library + +math.round = function(val) + return math.floor(val + 0.5) +end + --#endregion @@ -393,6 +466,17 @@ local is_dedicated_server --[ CONSTANTS ]-- --#region + +---@alias DLC_Name +---|'weapons' +---|'arid' + + +---@alias PreferenceType type +---|"text" + + + local SAVE_NAME = "CC_Autosave" local MAX_AUTOSAVES = 5 local STEAM_ID_MIN = "76561197960265729" @@ -426,7 +510,7 @@ local TYPE_ABBREVIATIONS = { string = "text", number = "num", table = "tbl", - bool = "bool", + boolean = "bool", playerID = "text/num", vehicle = "num", steamID = "num", @@ -504,6 +588,7 @@ local SLOT_LETTER_TO_NUMBER = { ---@field name string ---@field size integer 1 = Large, 2 = Small, 3 = Outfit ---@field data? EquipmentDataFloat|EquipmentDataInt +---@field dlc DLC_Name ---@class EquipmentDataFloat ---@field name string Name of data entry @@ -512,7 +597,7 @@ local SLOT_LETTER_TO_NUMBER = { ---@class EquipmentDataInt ---@field name string Name of data entry ----@field type "int"|"bool" +---@field type "int"|"boolean" ---@field default string|number --- @type Equipment[] @@ -653,7 +738,7 @@ local EQUIPMENT_DATA = { data = { int = { name = "on/off", - type = "bool", + type = "boolean", default = 0 } } @@ -733,7 +818,7 @@ local EQUIPMENT_DATA = { data = { int = { name = "on/off", - type = "bool", + type = "boolean", default = 0 }, float = { @@ -749,7 +834,7 @@ local EQUIPMENT_DATA = { data = { int = { name = "on/off", - type = "bool", + type = "boolean", default = 0 }, float = { @@ -765,7 +850,7 @@ local EQUIPMENT_DATA = { data = { int = { name = "on/off", - type = "bool", + type = "boolean", default = 0 }, float = { @@ -1351,19 +1436,19 @@ local FLUIDS = { -- [ DEFAULTS ]-- --#region ----@type { [string]: { value: any, type: type } } +---@type { [string]: { value: any, type: PreferenceType } } local PREFERENCE_DEFAULTS = { equipOnRespawn = { value = true, - type = "bool" + type = "boolean" }, keepInventory = { value = false, - type = "bool" + type = "boolean" }, removeVehicleOnLeave = { value = true, - type = "bool" + type = "boolean" }, maxVoxels = { value = 0, @@ -1403,15 +1488,15 @@ local PREFERENCE_DEFAULTS = { }, companion = { value = false, - type = "bool" + type = "boolean" }, companionInformationOnJoin = { value = false, - type = "bool" + type = "boolean" }, adminAll = { value = false, - type = "bool" + type = "boolean" } } @@ -1659,8 +1744,8 @@ function clamp(v, low, high) end --- converts strings to boolean values ----@param value string The string to convert to a bool ---- @return boolean value The input value as a bool +---@param value string The string to convert to a boolean +--- @return boolean value The input value as a boolean function toBool(value) local lookup = {["true"] = true, ["false"] = false} return lookup[string.lower(tostring(value))] @@ -1701,7 +1786,7 @@ end ---@param s string the string to be comparing against strings from table ---@param t table the table of strings to be comparing against s ---@param case_sensitive? boolean if the fuzzy search should be case sensitive or not ----@return string most_similar the most similar string from t +---@return string|false most_similar the most similar string from t function fuzzyStringInTable(s, t, case_sensitive) local match_exists = false local scores = {} @@ -1746,7 +1831,7 @@ end ---@param include_types boolean if the data types of the arguments should be included (include_arguments must be true) ---@param include_arg_descriptions boolean if the descriptions for the arguments should be included (include_arguments must be true) ---@param include_description boolean if the description of the command should be included ----@return string name the name of the command +---@return string|false name the name of the command ---@return string data the data of the command formatted for printing function prettyFormatCommand(command_name, include_arguments, include_types, include_arg_descriptions, include_description) local text = "" @@ -1805,10 +1890,10 @@ function checkTp(target_matrix) end ---Checks if the provided "playerID" is actually valid ----@param playerID string This could be a peerID, name, or "me" but it comes here as a string +---@param playerID string|Peer_ID This could be a peerID, name, or "me" but it comes here as a string ---@param caller Player The player that called this function. Used for translating "me" ---@return boolean is_valid If the provided playerID is valid ----@return number|nil Player The instance of the player +---@return Player|nil Player The instance of the player ---@return string|nil err Why the provided playerID is invalid function validatePlayerID(playerID, caller) playerID = tostring(playerID) @@ -1835,6 +1920,7 @@ function validatePlayerID(playerID, caller) for peerID, steamID in pairs(STEAM_IDS) do local player = G_players.get(steamID) + ---@cast player Player if peerIDs[player.name] then return false, nil, "Two players have the name " .. quote(playerID) .. ". Please use peerIDs instead" end @@ -1865,7 +1951,7 @@ function dataIsOfType(data, target_type, caller) if target_type == "playerID" then local success, player = validatePlayerID(data, caller) - return success, player, not success and " invalid playerID " .. tostring(data) + return success, player, not success and " invalid playerID " .. tostring(data) or nil elseif target_type == "vehicleID" then if not as_num then return false, nil, (tostring(data) .. " is not a number and therefor not a valid vehicleID") @@ -1891,15 +1977,15 @@ function dataIsOfType(data, target_type, caller) elseif target_type == "peerID" then return validatePlayerID(as_num, caller) elseif target_type == "number" then - return as_num ~= nil, as_num, not as_num and ((data or "nil") .. " is not a valid number") - elseif target_type == "bool" then + return as_num ~= nil, as_num, not as_num and ((data or "nil") .. " is not a valid number") or nil + elseif target_type == "boolean" then local as_bool = toBool(data) return as_bool ~= nil, as_bool, as_bool == nil and (tostring(data) .. " is not a valid boolean value") or nil elseif target_type == "letter" then local is_letter = isLetter(data) return is_letter, is_letter and data or nil, not is_letter and ((data or "nil") .. " is not a letter") or nil elseif target_type == "string" or target_type == "text" then - return data ~= nil, data or nil, data == nil and "nil is not a string" + return data ~= nil, data or nil, data == nil and "nil is not a string" or nil end return false, nil, ((data or "nil") .. " is not of a recognized data type") @@ -1910,7 +1996,9 @@ end ---@param target Player The player to run equip on ---@param notify boolean If the players involved should be notified of anything ---@param ... any equip args ----@return any results The results of `caller.equip()` +---@return boolean success +---@return string err Why the succeeded/failed +---@return string errText An explanation for why the operation succeeded/failed function equipArgumentDecode(caller, target, notify, ...) local args = {...} local args_to_pass = {} @@ -1944,17 +2032,19 @@ function paginate(page, data_table, entries_per_page) end ---Quotes text ----@param text string The text to put in quotes +---@param text string|number|boolean|nil The text to put in quotes ---@return string quoted_text The text in quotes function quote(text) return string.format("\"%s\"", tostring(text)) end ---Get the keys from a table and optionally sort them ----@param t table The table to get the keys from ----@param sort boolean If the keys should be sorted. Else, order is not guaranteed ----@param reverse_sort boolean If the keys should be sorted in ascending order rather than descending order ----@return table keys The keys from the table +---@generic K +---@generic V +---@param t table The table to get the keys from +---@param sort? boolean If the keys should be sorted. Else, order is not guaranteed +---@param reverse_sort? boolean If the keys should be sorted in ascending order rather than descending order +---@return table keys The keys from the table function getTableKeys(t, sort, reverse_sort) local keys = {} @@ -2259,15 +2349,21 @@ end ---Class that defines the object for each player ---@class Player +---@field peerID Peer_ID +---@field steamID Steam_ID +---@field name string +---@field banned Steam_ID|nil +---@field tp_blocking boolean +---@field show_vehicleIDs boolean local Player = {} --#region ---Creates a player object ---@param self Player This player object's instance ----@param peerID peerID The `peerID` of the player ----@param steamID steamID The `steamID` of the Player +---@param peerID Peer_ID The `peerID` of the player +---@param steamID Steam_ID The `steamID` of the Player ---@param name string The name of the player ----@param banned? steamID The steam_if of the admin that banned them or nil +---@param banned? Steam_ID The steam_if of the admin that banned them or nil function Player.constructor(self, peerID, steamID, name, banned) self.peerID = peerID self.steamID = steamID @@ -2331,7 +2427,7 @@ end ---Bans this player from the server ---@param self Player This player object's instance ----@param admin_steamID steamID The steam id of the admin banning this player +---@param admin_steamID Steam_ID The steam id of the admin banning this player ---@return boolean success If the operation succeeded or failed ---@return string err Why the operation succeeded/failed ---@return string errText Explanation for why the operation succeeded/failed @@ -2353,7 +2449,7 @@ end ---Unbans a player from the server ---@param self Player This player object's instance ----@param admin_steamID steamID The steam id of the admin banning this player +---@param admin_steamID Steam_ID The steam id of the admin banning this player ---@return boolean success If the operation succeeded or failed ---@return string err Why the operation succeeded/failed ---@return string errText Explanation for why the operation succeeded/failed @@ -2373,7 +2469,7 @@ end ---Gets the position of this player ---@param self Player This player object's instance ----@return matrix matrix The position of this player +---@return Transform matrix The position of this player ---@return boolean is_success If the position was retrieved successfully function Player.getPosition(self) return server.getPlayerPos(self.peerID) @@ -2381,7 +2477,7 @@ end ---Sets the position of this player ---@param self Player This player object's instance ----@param position matrix The target position of the player +---@param position Transform The target position of the player ---@return boolean success If the operation succeeded or failed ---@return string|nil err Why the operation failed, if it did ---@return string|nil errText Explanation for why the operation failed, if it did @@ -2408,7 +2504,9 @@ function Player.getInventory(self) return false, "ERROR", "Could not get the inventory of " .. self.prettyName() .. "\n::characterID could not be found" end + ---@type SLOT_NUMBER for i=1, #EQUIPMENT_SLOTS do + local equipment_id, success = server.getCharacterItem(character_id, i) inventory[i] = (success and equipment_id) or 0 if inventory[i] ~= 0 then @@ -2453,8 +2551,8 @@ end ---@param data2? number The second data of the item to insert ---@param is_active? boolean If the item should be active ---@return boolean success If the equip operation succeeded ----@return string err Why the succeeded/failed ----@return string errText An explanation for why the operation succeeded/failed +---@return string? err Why the succeeded/failed +---@return string? errText An explanation for why the operation succeeded/failed function Player.equip(self, notify, slot, item_id, data1, data2, is_active) local character_id, success = server.getPlayerCharacterID(self.peerID) @@ -2462,22 +2560,23 @@ function Player.equip(self, notify, slot, item_id, data1, data2, is_active) return false, "ERROR", "Could not find the character for " .. self.prettyName() .. ". This should never happen" end - item_id = tonumber(item_id) + local parsed_item_id = tonumber(item_id) slot = slot and string.upper(slot) or nil + ---@type SLOT_NUMBER local slot_number = SLOT_LETTER_TO_NUMBER[slot] - if not item_id then - return false, "INVALID ARG", "Could not convert argument " .. quote(item_id) .. " to an equipment_id (number)" + if not parsed_item_id then + return false, "INVALID ARG", "Could not convert argument " .. quote(parsed_item_id) .. " to an equipment_id (number)" end - if item_id == 0 then + if parsed_item_id == 0 then return server.setCharacterItem(character_id, slot_number or 1, 0, false, 0, 0) end - local item_data = EQUIPMENT_DATA[item_id] + local item_data = EQUIPMENT_DATA[parsed_item_id] if not item_data then - return false, "INVALID ARG", "There is no equipment with the id of " .. tostring(item_id) + return false, "INVALID ARG", "There is no equipment with the id of " .. tostring(parsed_item_id) end if item_data.dlc and not DLC[item_data.dlc] then return false, "DLC DISABLED", "The requested item " .. quote(item_data.name) .. " requires the " .. item_data.dlc .. " DLC" @@ -2488,10 +2587,11 @@ function Player.equip(self, notify, slot, item_id, data1, data2, is_active) local item_size = item_data.size local item_params = item_data.data local item_size_name = EQUIPMENT_SIZE_NAMES[item_size] - local item_pretty_name = string.format("\"%s\" (%d)", item_name, item_id) + local item_pretty_name = string.format("\"%s\" (%d)", item_name, parsed_item_id) local caller_pretty_name = notify and notify.prettyName() or nil local target_pretty_name = self.prettyName() + -- Todo: must be a string, but function parameter suggests it will be supplied as boolean here. is_active = toBool(is_active) or false -- Apply default charge etc. @@ -2527,10 +2627,10 @@ function Player.equip(self, notify, slot, item_id, data1, data2, is_active) if item_size == v.size then table.insert(available_slots, v.letter) if inventory[k] == 0 -- give player requested item in open slot - or inventory[k] == item_id -- replace an existing item, presumably to recharge it. + or inventory[k] == parsed_item_id -- replace an existing item, presumably to recharge it. or item_size ~= 2 -- item is not a small item, there is only one slot, replace the item then - if inventory[k] == item_id then isRecharge = true end + if inventory[k] == parsed_item_id then isRecharge = true end slot_number = k success = true break @@ -2550,7 +2650,7 @@ function Player.equip(self, notify, slot, item_id, data1, data2, is_active) end end - success = server.setCharacterItem(character_id, slot_number, item_id, is_active, data1, data2) + success = server.setCharacterItem(character_id, slot_number, parsed_item_id, is_active, data1, data2) if success then local slot_name = EQUIPMENT_SLOTS[slot_number].letter if notify then @@ -2629,7 +2729,7 @@ end ---Sets the state of tp blocking for this player ---@param self Player This player object's instance ----@param state? boolean The new state of tp blocking for this player. Toggles if no bool is provided +---@param state? boolean The new state of tp blocking for this player. Toggles if no boolean is provided ---@return boolean new_state The new state of tp blocking for this player function Player.setTpBlocking(self, state) if state == nil then @@ -2656,7 +2756,7 @@ end ---Set the new state for the vehicleID UI for this player ---@param self Player This player object's instance ----@param state? boolean The new state of the UI, enabled or disabled. Toggles if no bool is provided +---@param state? boolean The new state of the UI, enabled or disabled. Toggles if no boolean is provided ---@param show_server? boolean If server vehicles should be shown. Defaults to false ---@return boolean new_state The new state of the UI for this player function Player.setVehicleUIState(self, state, show_server) @@ -2731,6 +2831,7 @@ end ---Class that defines the object that contains all of the player objects ---@class PlayerContainer +---@field players table local PlayerContainer = {} --#region @@ -2742,8 +2843,8 @@ end ---Creates a new player object and adds it to this container ---@param self PlayerContainer This player container's object instance ----@param peerID peerID The peerID of the player ----@param steamID steamID The steamID of the player +---@param peerID Peer_ID The peerID of the player +---@param steamID Steam_ID The steamID of the player ---@param name string The name of the player ---@param banned? boolean If the player should be banned immediately ---@return Player self The new player object @@ -2767,6 +2868,21 @@ end ---Class that defines the object for each vehicle ---@class Vehicle +---@field vehicleID Vehicle_ID the server unique ID for this vehicle. +---@field owner Steam_ID the owner of the vehicle, could be -1 for server/addon spawned. +---@field server_spawned boolean true if the server spawned the vehicle. +---@field name string the name of the vehicle according to the server. +---@field pretty_name string the name of the vehicle if it has one or it's Vehicle ID. +---@field cost number the cost of the vehicle +---@field ui_id integer the id of the user interface element assigned to this object. +---@field static boolean is the vehicle made static. +---@field x number gpsX +---@field y number gpsY +---@field z number gpsAlt +---@field needsSync boolean the vehicle state needs to be synced. +---@field lastSyncX number last value that was synced for x +---@field lastSyncY number last value that was synced for y +---@field lastSyncAlt number last value that was synced for z local Vehicle = {} --#region @@ -2787,6 +2903,8 @@ function Vehicle.constructor(self, vehicleID, owner_steamID, cost) self.name = success and (name ~= "Error" and name or "Unknown") or "Unknown" self.pretty_name = string.format("%s(%d)", self.name, self.vehicleID) self.cost = exploreTable(g_savedata.vehicles, {vehicleID, "cost"}) or cost + local data, success = server.getVehicleData(vehicleID) + self.static = success and data.static or false self.ui_id = exploreTable(g_savedata.vehicles, {vehicleID, "ui_id"}) or server.getMapID() @@ -2804,7 +2922,7 @@ end ---@param voxel_x number Voxel x axis offset ---@param voxel_y number Voxel y axis offset ---@param voxel_z number Voxel z axis offset ----@return matrix|boolean matrix The matrix of the vehicle or false if the position could not be found +---@return Transform|boolean matrix The matrix of the vehicle or false if the position could not be found function Vehicle.getPosition(self, voxel_x, voxel_y, voxel_z) local position, success = server.getVehiclePos(self.vehicleID, voxel_x, voxel_y, voxel_z) return success and position or false @@ -2812,7 +2930,7 @@ end ---Sets the position of the vehicle ---@param self Vehicle This vehicle's object instance ----@param position matrix The new position to set for the vehicle +---@param position Transform The new position to set for the vehicle ---@param unsafe boolean If the vehicle should be teleported to the exact position, not accounting for any obstacles ---@return boolean success If the vehicle was successfully teleported function Vehicle.setPosition(self, position, unsafe) @@ -2835,6 +2953,7 @@ end ---Class that defines the object that contains all of the vehicle objects ---@class VehicleContainer +---@field vehicles table local VehicleContainer = {} --#region @@ -2846,8 +2965,8 @@ end ---Creates a new vehicle object and adds it to this container ---@param self VehicleContainer This vehicle container's object instance ----@param vehicleID vehicleID The vehicleID of the vehicle ----@param owner_steamID steamID The steamID of the player that owns the vehicle +---@param vehicleID Vehicle_ID The vehicleID of the vehicle +---@param owner_steamID Steam_ID The steamID of the player that owns the vehicle ---@param cost number The cost of the vehicle ---@return Vehicle vehicle The new vehicle object function VehicleContainer.create(self, vehicleID, owner_steamID, cost) @@ -2857,7 +2976,7 @@ end ---Removes a vehicle object from this vehicle container object ---@param self VehicleContainer This vehicle container's object instance ----@param vehicleID vehicleID The vehicleID of the vehicle to remove +---@param vehicleID Vehicle_ID The vehicleID of the vehicle to remove function VehicleContainer.remove(self, vehicleID) local vehicle = self.get(vehicleID) if not vehicle then return end @@ -2880,7 +2999,7 @@ end ---Gets a vehicle from this container ---@param self VehicleContainer This vehicle container's object instance ----@param vehicleID? vehicleID The vehicleID of the vehicle to get +---@param vehicleID? Vehicle_ID The vehicleID of the vehicle to get ---@param list_server? boolean If vehicles spawned by the server should be returned ---@return Vehicle|table|nil vehicle The vehicle you were looking for or nil if the vehicle could not be found. If the `vehicleID` argument is not provided, a table containing all vehicles in this container will be returned function VehicleContainer.get(self, vehicleID, list_server) @@ -2956,7 +3075,7 @@ end ---Prints the list of rules to a player ---@param self Rules This rule container object's instance ----@param steamID steamID The steamID of the player to print the rules to +---@param steamID Steam_ID The steamID of the player to print the rules to ---@param page number The page of the rulebook to print. If 0, all rules are printed ---@param silent boolean If there are no rules, nothing will be announced function Rules.print(self, steamID, page, silent) @@ -2993,7 +3112,6 @@ end --[ CALLBACK FUNCTIONS ]-- --#region - g_savedata.version = SaveDataVersion g_savedata.autosave = 1 g_savedata.is_dedicated_server = false @@ -3089,9 +3207,12 @@ function onCreate(is_new) local adminAll = property.checkbox("Admin all players", "false") local everyoneRole = G_roles.get("Everyone") + if everyoneRole then everyoneRole.setPermissions(adminAll, everyoneRole.auth) G_preferences.adminAll.value = true + else + companionError("Everyone role not found, can not admin everyone!") end end @@ -3126,7 +3247,12 @@ function onCreate(is_new) TILE_POSITIONS = getTilePositions() - DLC = {weapons = server.dlcWeapons()} + ---@type table + DLC = + { + weapons = server.dlcWeapons(), + arid = server.dlcArid(), + } autosave() end @@ -3159,9 +3285,11 @@ function chatLogAppendMessage(steamID, message) end function triggerChatLogSend() - sendToServer("stream-chat", chatBuffer) - chatBuffer = {} - ticksSinceLastChatLogSent = 0 + local success = sendToServer("stream-chat", chatBuffer) + if success then + chatBuffer = {} + ticksSinceLastChatLogSent = 0 + end end function onDestroy() @@ -3306,7 +3434,8 @@ end function onPlayerRespawn(peerID) if invalid_version then - server.announce("WARNING", "Your code is older than your save data. To prevent data loss/corruption, no data will be processed. Please update Carsa's Commands to the latest version.") + server.announce("WARNING", "Your code is older than your save data. To prevent data loss/corruption, no data will be processed. Please update Carsa's Commands to the latest version.\n" + .."Script data version: "..ScriptVersion.." save data version: "..(g_savedata.version or "unknown")) return end @@ -3366,15 +3495,38 @@ function onVehicleSpawn(vehicleID, peerID, x, y, z, cost) G_vehicles.create(vehicleID, -1, cost) end - syncData('vehicles') + -- Assign position for companion map tracking + local vehicle = G_vehicles.get(vehicleID, true) + if vehicle then + vehicle.needsSync = true + vehicle.x = x + vehicle.y = y + vehicle.z = z + end + +end + +function onVehicleTeleport(vehicleID, peerID, x, y, z) + local vehicle = G_vehicles.get(vehicleID, true) + + if not vehicle then return end + + vehicle.needsSync = true + vehicle.x = x + vehicle.y = y + vehicle.z = z + end +-- holds all despawned vehicles, so stream-map can send this info to the clients +despawnedVehicles = {} + function onVehicleDespawn(vehicleID, peerID) if invalid_version then return end - G_vehicles.remove(vehicleID) + table.insert(despawnedVehicles, vehicleID) - syncData('vehicles') + G_vehicles.remove(vehicleID) end --- This triggers for both press and release events, but not while holding. @@ -3469,7 +3621,9 @@ function onTick() registerCompanionCommandCallback("command-sync-all", function(token, com, content) for k, v in pairs(SYNCABLE_DATA) do - syncData(k) + if k ~= 'TILE_POSITIONS' or content == "FORCE_TILES!" then-- do not sync tiles, unless servers asks to (for caching) + syncData(k) + end end triggerTokenSync() @@ -3492,6 +3646,7 @@ function onTick() local player = G_players.get(steamid) if player.banned then + --Todo: Returns don't match signature. return false, "Invalid token", "You are banned" end @@ -3982,8 +4137,8 @@ COMMANDS = { category = "Roles", args = { {name = "role", type = {"string"}, required = true, description = "The name of the role to modify."}, - {name = "is_admin", type = {"bool"}, required = true, description = "If this role should grant admin privileges."}, - {name = "is_auth", type = {"bool"}, required = true, description = "If this role should be authorized."} + {name = "is_admin", type = {"boolean"}, required = true, description = "If this role should grant admin privileges."}, + {name = "is_auth", type = {"boolean"}, required = true, description = "If this role should be authorized."} }, description = "Sets the permissions of a role.", syncableData = {"roles"} @@ -4022,7 +4177,7 @@ COMMANDS = { args = { {name = "role", type = {"string"}, required = true, description = "The name of the role to modify."}, {name = "command", type = {"string"}, required = true, description = "The command to grant/revoke access to."}, - {name = "value", type = {"bool"}, required = true, description = "If the role should have access to the command."} + {name = "value", type = {"boolean"}, required = true, description = "If the role should have access to the command."} }, description = "Sets whether a role has access to a command or not.", syncableData = {"roles"} @@ -4177,7 +4332,7 @@ COMMANDS = { category = "Roles", args = { {name = "role", type = {"string"}, required = true, description = "The name of the role to modify."}, - {name = "status", type = {"bool"}, description = "If the role is enabled or disabled."} + {name = "status", type = {"boolean"}, description = "If the role is enabled or disabled."} }, description = "Gets or sets whether a role is active or not. An inactive role won't apply it's permissions to it's members", syncableData = {"roles"} @@ -4245,7 +4400,7 @@ COMMANDS = { local succeeded = {} for _, vehicle in ipairs(vehicles) do local success = false - if G_vehicles:get(vehicle).owner == caller.steamID then + if G_vehicles.get(vehicle).owner == caller.steamID then success = server.despawnVehicle(vehicle.vehicleID, true) end if success then @@ -4278,7 +4433,7 @@ COMMANDS = { category = "Vehicles", args = { {name = "vehicleID", type = {"vehicleID"}, required = true, description = "The vehicle to modify."}, - {name = "true/false", type = {"bool"}, required = true, description = "If the vehicle can be edited or not."} + {name = "true/false", type = {"boolean"}, required = true, description = "If the vehicle can be edited or not."} }, description = "Sets a vehicle to either be editable or non-editable.", syncableData = {"vehicles"} @@ -4312,7 +4467,7 @@ COMMANDS = { category = "Vehicles", args = { {name = "page", type = {"number"}, description = "The page to show."}, - {name = "list_server", type = {"bool"}, description = "If vehicles spawned by the server and other addons should be listed."} + {name = "list_server", type = {"boolean"}, description = "If vehicles spawned by the server and other addons should be listed."} }, description = "Lists all the vehicles that are spawned in the game." }, @@ -4323,7 +4478,7 @@ COMMANDS = { end, category = "Vehicles", args = { - {name = "list_server", type = {"bool"}, description = "If vehicles spawned by the server and other addons should be displayed."} + {name = "list_server", type = {"boolean"}, description = "If vehicles spawned by the server and other addons should be displayed."} }, description = "Toggles displaying vehicle IDs." }, @@ -4561,7 +4716,7 @@ COMMANDS = { {name = "data1", type = {"number"}, description = "The first data slot of the item."}, {name = "data2", type = {"number"}, description = "The second data slot of the item."}, - {name = "active", type = {"bool"}, description = "If the item is active."} + {name = "active", type = {"boolean"}, description = "If the item is active."} }, description = "Equips you with the requested item." }, @@ -4577,7 +4732,7 @@ COMMANDS = { {name = "data1", type = {"number"}, description = "The first data slot of the item."}, {name = "data2", type = {"number"}, description = "The second data slot of the item."}, - {name = "active", type = {"bool"}, description = "If the item is active."} + {name = "active", type = {"boolean"}, description = "If the item is active."} }, description = "Equips the specified player with the requested item." }, @@ -4767,7 +4922,7 @@ COMMANDS = { category = "Teleporting", args = { {name = "vehicleID", type = {"vehicleID"}, required = true, description = "The vehicle to teleport to your position."}, - {name = "unsafe", type = {"bool"}, description = "If the vehicle should be teleported to your exact location, ignoring surroundings."} + {name = "unsafe", type = {"boolean"}, description = "If the vehicle should be teleported to your exact location, ignoring surroundings."} }, description = "Teleports a vehicle to you." }, @@ -5066,7 +5221,7 @@ COMMANDS = { end, category = "Preferences", args = { - {name = "confirm", type = {"bool"}, required = true, description = "Confirms this action."} + {name = "confirm", type = {"boolean"}, required = true, description = "Confirms this action."} }, description = "Resets all server preferences back to their default states. Be very careful with this command as it can drastically change how the server behaves.", syncableData = {"preferences"} @@ -5095,7 +5250,7 @@ COMMANDS = { end local target_type = preference.type - if target_type == "bool" then + if target_type == "boolean" then local val = toBool(args[1]) if val ~= nil then preference.value = val @@ -5127,13 +5282,13 @@ COMMANDS = { return true, "PREFERENCE EDITED", preference_name .. " has been set to " .. tostring(preference.value) else -- there was an incorrect type - return false, "INVALID ARG", preference_name .. " only accepts a " .. preference.type .. " as its value" + return false, "INVALID ARG", preference_name .. " only accepts a " .. preference.type .. " as its value, '"..tostring(args[1]).."' could not be converted to " .. preference.type .."." end end, category = "Preferences", args = { {name = "preference_name", type = {"string"}, required = true, description = "The name of the preference to edit"}, - {name = "value", type = {"bool", "number", "text"}, required = true, description = "The new value of the preference"} + {name = "value", type = {"boolean", "number", "text"}, required = true, description = "The new value of the preference"} }, description = "Sets the specified preference to the requested value. Use ?preferences to see all of the preferences.", syncableData = {"preferences"} @@ -5255,7 +5410,7 @@ COMMANDS = { category = "Game Settings", args = { {name = "setting_name", type = {"string"}, required = true, description = "The name of the setting to change."}, - {name = "value", type = {"bool"}, required = true, description = "The new value of the setting."} + {name = "value", type = {"boolean"}, required = true, description = "The new value of the setting."} }, description = "Sets the specified game setting to the requested value.", syncableData = {"gamesettings"} @@ -5622,9 +5777,11 @@ function companionLogAppendMessage(msg) end function triggerCompanionLogSend() - sendToServer("stream-log", logBuffer) - logBuffer = {} - ticksSinceLastCompanionLogSent = 0 + local success = sendToServer("stream-log", logBuffer) + if success then + logBuffer = {} + ticksSinceLastCompanionLogSent = 0 + end end @@ -5722,14 +5879,36 @@ local packetToServerIdCounter = 0 local pendingPacketParts = {} local lastSentPacketPartHasBeenRespondedTo = false local lastSentPacketIdent = nil --- @data: table, string, number, bool (can be multidimensional tables; circular references not allowed!) --- @meta: a table of additional fields to be send to the server --- @callback: called once server responds callback(success, response) --- @ignoreServerNotAvailable: only used by heartbeat! --- --- --- returns true --if your data will be sent --- returns false, "error message" --if not + +---@alias MessageType +---|"stream-chat" +---|"test-performance-game-backend" +---|"token-sync" +---|"stream-log" +---|"check-notifications" +---|"heartbeat" +---|"get-companion-url" +---|"stream-map" +---|"command-response" +---|"players" +---|"rules" +---|"roles" +---|"vehicles" +---|"gameSettings" +---|"preferences" +---|"commands" +---|"TILE_POSITIONS" +---|"SCRIPT_VERSION" + + +---@param datatype MessageType +---@param data table|string|number|boolean Nested tables allowed, but no reference identity is not preserved and loops are not allowed. +---@param meta table|nil a table of additional fields to be send to the server +---@param callback fun(success: boolean, response: table) | nil called once server responds +---@param ignoreServerNotAvailable boolean|nil only used by heartbeat! +---@param prioritizeMessageInQueue boolean|nil +---@return boolean success Data will be sent +---@return string? errorInfo Error message when not `success` function sendToServer(datatype, data, meta --[[optional]], callback--[[optional]], ignoreServerNotAvailable--[[optional]], prioritizeMessageInQueue--[[optional]]) --[[ @@ -5754,7 +5933,7 @@ function sendToServer(datatype, data, meta --[[optional]], callback--[[optional] packetToServerIdCounter = packetToServerIdCounter + 1 if callback and not (type(callback) == "function") then - return false, "callback must be a function" + return false, "callback must be a function or nil" end if not ignoreServerNotAvailable and not serverIsAvailable then @@ -5854,9 +6033,18 @@ function sendToServer(datatype, data, meta --[[optional]], callback--[[optional] return true end + +---@alias CompanionCommandCallbackFunction +---|fun(playerToken: any, commandName: string, commandContent: any): boolean, string +---|fun(playerToken: any, commandName: string, commandContent: any): true + + companionCommandCallbacks = {} -- @callback: this function must return: success (boolean), message (string, optional) -- callback() is called like this: function (playertoken, commandname, commandcontent) +---@param commandname any +---@param callback CompanionCommandCallbackFunction +---@return nil function registerCompanionCommandCallback(commandname, callback) if not (type(callback) == "function") then return companionError("@registerCompanionCommandCallback: callback must be a function") @@ -5884,6 +6072,7 @@ end local lastPacketSentTickCallCount = 0 local tickCallCounter = 0 local HTTP_MAX_TIME_NECESSARY_BETWEEN_REQUESTS = 60 --in case we have a problem inside httpReply, and don't detect that the last sent message was replied to, then allow another request after this time +local delay_between_packet_queue_complaints = 60 * 20 -- seconds function checkPacketSendingQueue() tickCallCounter = tickCallCounter + 1 if (#packetSendingQueue > 0) and (lastSentPacketPartHasBeenRespondedTo or (lastPacketSentTickCallCount == 0) or (tickCallCounter - lastPacketSentTickCallCount > HTTP_MAX_TIME_NECESSARY_BETWEEN_REQUESTS) ) then @@ -5907,7 +6096,8 @@ function checkPacketSendingQueue() end end - if tickCallCounter % 60 * 5 == 0 and #packetSendingQueue > 500 then + if tickCallCounter % delay_between_packet_queue_complaints == 0 + and #packetSendingQueue > 500 then local typeCounts = {} for _, packet in pairs(packetSendingQueue) do @@ -5989,6 +6179,8 @@ function requestCompanionUrl() isRequestingCompanionUrl = true +-- Justification: Special case: suppressing the error for this one case is better than changing the type to allow it everywhere (where it would likely be wrong). +---@diagnostic disable-next-line: param-type-mismatch sendToServer("get-companion-url", nil, nil, function (success, response) if success then COMPANION_URL = response @@ -5998,10 +6190,14 @@ function requestCompanionUrl() end, true) end -local LIVESTREAM_TIME_BETWEEN = 60 * 2 -local lastLiveStreamSentTick = 0 +--- Aim to send updates this often. +local mapStreamIntervalTicks = 60 * 2 + +--- Tick when last sent. +local mapStreamLastSentTick = 0 + function triggerMapStream() - lastLiveStreamSentTick = tickCallCounter + mapStreamLastSentTick = tickCallCounter local streamData = { playerPositions = {}, @@ -6011,18 +6207,61 @@ function triggerMapStream() for _, player in pairs(server.getPlayers()) do local matrix, success = server.getPlayerPos(player.id) if success and matrix then - streamData.playerPositions[player.steam_id] = {x = matrix[13], y = matrix[15], alt = matrix[14]} + streamData.playerPositions[player.steam_id] = { + x = math.round(matrix[13]), + y = math.round(matrix[15]), + alt = math.round(matrix[14]) + } end end for vehicleID, vehicle in pairs(G_vehicles.vehicles) do - local matrix, success = server.getVehiclePos(vehicleID) - if success and matrix then - streamData.vehiclePositions[vehicleID] = {x = matrix[13], y = matrix[15], alt = matrix[14]} + + if vehicle.static then + if vehicle.needsSync then + vehicle.needsSync = false + + streamData.vehiclePositions[vehicleID] = { + x = math.round(vehicle.x), + y = math.round(vehicle.z), + alt = math.round(vehicle.y), + static = true + } + end + else + local matrix, success = server.getVehiclePos(vehicleID) + if success and matrix then + + local newX = math.round(matrix[13]) + local newY = math.round(matrix[15]) + local newAlt = math.round(matrix[14]) + + -- only sync when position has changed at least 1 meter in any direction + if newX ~= vehicle.lastSyncX or newY ~= vehicle.lastSyncY or newAlt ~= vehicle.lastSyncAlt then + + vehicle.lastSyncX = newX + vehicle.lastSyncY = newY + vehicle.lastSyncAlt = newAlt + + streamData.vehiclePositions[vehicleID] = { + x = newX, + y = newY, + alt = newAlt + } + end + end end end - sendToServer("stream-map", streamData, nil, nil, true) + if #despawnedVehicles > 0 then + streamData.deletedVehicles = despawnedVehicles + end + + local success = sendToServer("stream-map", streamData) + + if success then + despawnedVehicles = {} + end end -- must be called every onTick() @@ -6056,7 +6295,8 @@ function syncTick() ticksSinceLastChatLogSent = ticksSinceLastChatLogSent + 1 -- stream live data - if (tickCallCounter - lastLiveStreamSentTick) > LIVESTREAM_TIME_BETWEEN then + if (tickCallCounter - mapStreamLastSentTick) > mapStreamIntervalTicks + then triggerMapStream() end end