--[[ Library for New Mode scripts with `[TWEAKS] ALT_ONLINE_CARS=` parameter. Use it to define how many fake cars to add, and then use this library to control those cars remotely by sending packets with updated car state and timestamp (CSP will use AC logic to extrapolate car motion). To set ID of a car, use `[TWEAKS] ALT_ONLINE_CAR_ID=…`. Look at “p2p-1v1” mode for an example. To use, include with `local altonline = require('shared/sim/altonline')`. ]] ---@diagnostic disable local altonline = {} ---Check if a certain car is connected. ---@param carIndex integer @0-based car index. ---@return boolean function altonline.isDriverConnected(carIndex) return __util.native() end ---Driver has connected: change name, car, show a message (unless `silent` is `true`). ---If `carID` differs from currently loaded car ID, car will be swapped. If `carID` doesn’t exist, AC will crash. ---Add `temporary: 'car'|'skin'` and CSP will remove the car or the skin after the car has been loaded (only if it’s been created in the last hour or so). ---Callback `carLoadedCallback` is fired only when car loading is finished. If it’s cancelled, it won’t be triggered. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param args {driverName: string, driverTeam: string?, driverNation: string?, carID: string?, carSkinID: string, temporary: nil|'car'|'skin', silent: boolean?, carLoadedCallback: fun()} function altonline.driverConnected(carIndex, args) __util.native() end ---Driver has disconnected: hide car, show a message (unless `silent` is `true`). ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param args {silent: boolean?}? function altonline.driverDisconnected(carIndex, args) __util.native() end ---New car state is available: smoothly interpolate into it. Value of `timestamp` should be something like ---`ac.getSim().time` with age of state in milliseconds subtracted. Value of `packetID` is optional: if you ---decide to use it, increment it for each new state, and CSP will drop any packets that have a packetID ---that are not above previously seen packets (in case you’re using something like UDP connection where ---packets can arrive out of order). --- ---To make sure cars are moving smoothly and without jumps, pass accurate `velocity`. --- ---Note: to improve performance, it might be benefitial to reuse the table. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param args {position: vec3, rotation: vec3, velocity: vec3, timestamp: number, packetID: integer?, ping: integer?, steerAngle: number?, wheelAngle: number?, engineRPM: number?, gear: integer?, wheelAngularSpeed: number[], wheelSlipping: boolean[], statusBytes: integer?, headlightsActive: boolean?, brakeLightsActive: boolean?, hornActive: boolean?} ---@return number? @Age of a state packet in milliseconds (can be used as ping), or `nil` if target car isn’t connected. function altonline.submitState(carIndex, args) return __util.native() end ---Set car damage. Usually, expects four numbers with damage amount in km/h. Passed via a separate function, ---because this piece of car state changes rarely. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param args number|number[] function altonline.setDamage(carIndex, args) __util.native() end ---Set car tyre compounds. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param args string @Short compound name. function altonline.setTyres(carIndex, shortCompoundName) __util.native() end ---Set car ping. If used on remote car, overrides value from its state if it’s 0. If used on own car, creates QoS ---HUD element (ping counter in the upper right corner). ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param value integer @Ping in milliseconds. function altonline.setPing(carIndex, value) __util.native() end ---Show a chat message received from a certain car. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param message string function altonline.chatMessageFrom(carIndex, message) __util.native() end ---Show a chat message from the “server”. ---@param message string function altonline.serverChatMessage(message) __util.native() end ---Call this function from `.onSendingChatMessage()` callback to report that the message couldn’t be sent. ---@param message string function altonline.failedToSendChatMessage(message) __util.native() end ---Sets a callback that will be called when user sends a message in chat. ---@param callback fun(msg: string) ---@return ac.Disposable function altonline.onSendingChatMessage(callback) return __util.native() end ---Return car state flags packed into a 32-bit integer. Pass them as `statusBytes` to `.submitState()` to sync state of headlights, brake lights, ---horn, ERS, DRS, slipping wheels, turning lights, wipers, extra CSP switches and more. --- ---Ideally, try to make sure all returned status values are sent, without skipping flags (odd and even calls return different values, for example). ---@param carIndex integer @0-based car index. ---@return integer function altonline.getCarStatusBytes(carIndex) return __util.native() end ---Change pit spot assigned to a car. Can be used for both local and remote cars. ---@param carIndex integer @0-based car index. ---@param pitIndex integer @0-based pits index. ---@param moveCar boolean? @Set to `false` to stop a local car from moving to new pits position if it’s been in the old pits position. Default value: `true`. function altonline.setPitIndex(carIndex, pitIndex, moveCar) __util.native() end local timeReferenceConfigured = false ---Call this function and pass it accurate time (synced on all clients) and it’ll ---be used by `altonline.packCarState1()`/`altonline.submitPackedState1()` to account ---for networking delays. ---@param worldTime number|string|string[] @Time in seconds synced on all clients (pretty much any number, only requirement is that if first client calls it with X and second client calls it, let’s say, 5 seconds later, it should pass `X + 5`). Alternatively, pass an NTP server or a few and it’ll load accurate time and pass that. function altonline.configureTimeReference(worldTime) if type(worldTime) == 'number' then __util.native() elseif not timeReferenceConfigured then timeReferenceConfigured = true require('shared/utils/ntp').query(worldTime or '', 6, function (err, epochTimeS) if err then ac.warn('NTP request failed: %s' % err) else altonline.configureTimeReference(epochTimeS) end end) end end ---Encode current time (such as `ac.getSim().time`, milliseconds from the start of AC) as `uint32` to send ---to another client. Both clients should have configured time reference for this mechanism to work. If ---something is off, returns 0. Meant for an event to be encoded and send over, the result doesn’t remain ---valid for long (especially with `uint16`, it will be valid for about 30 seconds, but if your delay between ---clients is above 30 seconds, realistically you have bigger fish to fry). ---@param time number @Time in milliseconds. ---@param compact boolean? @Pass `true` to encode it as `uint16` instead. ---@return integer @Encoded timestamp. function altonline.timeToTimestamp(time, compact) return __util.native() end ---Decode encoded timestamp on a different client. If 0 is passed, or if time reference is not configured, ---returns current time (pretty much `ac.getSim().time`). ---@param timestamp integer @Encoded timestamp. ---@param compact boolean? @Pass `true` if timestamp was encoded as `uint16`. ---@return number @Time in milliseconds. function altonline.timestampToTime(timestamp, compact) return __util.native() end ---Pack car state into a few bytes ready to be shipped to a different client. ---Use `altonline.configureTimeReference()` on both clients once after loading ---to ensure state is properly in sync, accounting for any networking delays. ---First version uses 36 bytes for the whole car state, pretty extreme compression. ---Can be called from physics worker. ---@param carIndex integer @0-based car index. ---@return binary function altonline.packCarState1(carIndex) return __util.native() end ---Similar to `altonline.submitState()`, but uses a state prepared by ---`altonline.packCarState1()`. Can be called from physics worker. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param state binary ---@return number? @Age of a state packet in milliseconds (can be used as ping), or `nil` if target car isn’t connected. function altonline.submitPackedState1(carIndex, state) return __util.native() end ---Ensure physics simulation can’t be paused. If it’s currently paused, it’ll unpause. Doesn’t ---affect pause menu, just makes it so that sim keeps going while the pause menu is opened. ---@param block boolean? @Default value: `true`. function altonline.blockPause(block) __util.native() end ---Disable session control entirely. ---@param block boolean? @Default value: `true`. function altonline.blockSessions(block) __util.native() end ---Report a completed lap. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param data {lapTime: integer, cuts: integer?, lapsCount: integer?, splits: integer[]?, lineCrossTimestamp: integer, valid: boolean?} function altonline.onLapCompleted(carIndex, data) __util.native() end ---Pack all the laps driven in this session into a compact binary form. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@return binary function altonline.packLaps(carIndex) return __util.native() end ---Apply packed laps. ---@param carIndex integer @0-based car index. Raises an error if index doesn’t correspond to a fake car. ---@param data binary? ---@return boolean function altonline.unpackLaps(carIndex, data) return __util.native() end ---Set extended CSP online config. Not all settings will work. You can only call it once, in a first second or so after loading. ---@param config string|ac.INIConfig ---@return boolean function altonline.configure(config) return __util.native('__altonline', 'configure', 0, tostring(config)) end ---Set a callback that will be called when CSP wants to send a small chunk of data to all clients, or to a certain client. ---Usually, those are used for exchanging extra data, such as annoucing a car color change, or blown tyres state change. ---Originally, CSP would use special chat messages to send that data, so the packets are usually small and rare. ---@param callback fun(args: {data: binary, from: integer?, to: integer?, unreliable: boolean?, range: number?}) @If `from` is `nil`, this message is coming not from a certain car, but from a user (like setup shared in chat). If `to` is `nil`, it’s expected that you would send this message to all clients (but this one). If `unreliable` is `false`, please try your best to deliver the message, otherwise feel free to use something like UDP. If `range` is set, you can use it to send the message only to clients within that radius (`range` can’t be set without `unreliable` flag). ---@return ac.Disposable function altonline.onSendingExtraMessage(callback) return __util.native() end ---A counterpart to `altonline.onSendingExtraMessage()` for receiving messages on the opposing side. ---@param carIndex integer @0-based origin car index. ---@param data binary @Data, field `data` from `args` when `callback` was called (please to not alter the data). function altonline.extraMessageFrom(carIndex, data) __util.native() end return altonline