From 8bdec81a0ad106d73d45df7fd9a83c4b7f08b7f5 Mon Sep 17 00:00:00 2001 From: DaddelZeit <141176220+DaddelZeit@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:18:55 +0200 Subject: [PATCH 1/6] Implement kissghosts to centrally control ghost state --- .../lua/ge/extensions/kissghosts.lua | 123 ++++++++++++++++++ .../extensions/kiss_mp/kiss_vehicle.lua | 14 ++ KISSMultiplayer/scripts/kiss_mp/modScript.lua | 1 + 3 files changed, 138 insertions(+) create mode 100644 KISSMultiplayer/lua/ge/extensions/kissghosts.lua diff --git a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua new file mode 100644 index 00000000..377ca33c --- /dev/null +++ b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua @@ -0,0 +1,123 @@ +local M = {} + +local ghostSpawnsQueued = {} + +M.veh_to_ghost_map = {} +M.id_is_ghost = {} +M.ghost_state = {} +M.pause_overrides = {} + +local actual_ghost_states = {} +local function set_vehicle_ghost(veh_id, ghost_state, mesh_fade) + if M.pause_overrides[veh_id] then + -- we still want M.ghost_state to keep its original value + ghost_state = 2 + else + M.ghost_state[veh_id] = ghost_state + end + + if actual_ghost_states[veh_id] == ghost_state then return end + actual_ghost_states[veh_id] = ghost_state + + local veh = getObjectByID(veh_id) + + if ghost_state == 0 then -- no ghost + veh:setActive(1) + veh:queueLuaCommand("kiss_vehicle.set_collision(true)") + elseif ghost_state == 1 then -- no collisions + veh:setActive(1) + veh:queueLuaCommand("kiss_vehicle.set_collision(false)") + elseif ghost_state == 2 then -- in stasis (more logic for this state is in onUpdate) + veh:setActive(1) + veh:queueLuaCommand("kiss_vehicle.set_collision(false)") + elseif ghost_state == 3 then -- tucked somewhere in a parallel universe + veh:setActive(0) + end + + if type(mesh_fade) ~= "number" then + mesh_fade = mesh_fade and 0.5 or 1 + end + veh:setMeshAlpha(mesh_fade, "") +end + +local function onUpdate() + for id, v in pairs(M.pause_overrides) do + -- 0.75 appears to be a safe value + -- values too small cause breakage + local veh = getObjectByID(id) + if veh and v then + veh:applyClusterVelocityScaleAdd(0, 0.75, 0, 0, 0) + end + end +end + +local function set_pause_override(id, bool) + if not bool then + M.pause_overrides[id] = nil + else + M.pause_overrides[id] = true + end + set_vehicle_ghost(id, M.ghost_state[id], bool) +end + +local velocity = vec3() +local function set_pause_override_for_owned(bool) + for id in pairs(vehiclemanager.ownership) do + local vehicle = getObjectByID(id) + velocity:set(vehicle:getVelocityXYZ()) + if velocity:length() < 1 then + set_pause_override(id, bool) + end + end +end + +local pause_counter = 0 +local pause_requests = {} + +local function attempt_pause(id) + if pause_requests[id] then + return + end + pause_requests[id] = true + pause_counter = pause_counter + 1 + if pause_counter > 0 then + set_pause_override_for_owned(true) + end +end + +local function attempt_unpause(id) + if not pause_requests[id] then + return + end + pause_requests[id] = nil + pause_counter = math.max(0, pause_counter - 1) + + if pause_counter == 0 then + set_pause_override_for_owned(false) + end +end + +local function onVehicleSpawned(vehId) + if not network.connection.connected then return end + ghostSpawnsQueued[#ghostSpawnsQueued+1] = vehId +end + +local function onVehicleDestroyed(vehId) + M.pause_overrides[vehId] = nil +end + +local function onExtensionLoaded() +end + +M.onUpdate = onUpdate +M.onExtensionLoaded = onExtensionLoaded +M.onVehicleSpawned = onVehicleSpawned +M.onVehicleDestroyed = onVehicleDestroyed + +M.set_vehicle_ghost = set_vehicle_ghost +M.set_pause_override = set_pause_override + +M.attempt_pause = attempt_pause +M.attempt_unpause = attempt_unpause + +return M \ No newline at end of file diff --git a/KISSMultiplayer/lua/vehicle/extensions/kiss_mp/kiss_vehicle.lua b/KISSMultiplayer/lua/vehicle/extensions/kiss_mp/kiss_vehicle.lua index 0df90db1..cae45430 100644 --- a/KISSMultiplayer/lua/vehicle/extensions/kiss_mp/kiss_vehicle.lua +++ b/KISSMultiplayer/lua/vehicle/extensions/kiss_mp/kiss_vehicle.lua @@ -5,6 +5,8 @@ local string_buffer = require("string.buffer") local nodes = {} local ref_nodes = {} +M.collision = true + local last_node = 1 local nodes_per_frame = 32 @@ -155,11 +157,23 @@ local function send_vehicle_config() objectId, jsonEncode(config), string_buffer.encode(data))) end +local function set_collision(collision) + M.collision = collision + + if collision then + obj:setGhostEnabled(false) + else + obj:setGhostEnabled(true) + end +end + M.update_transform_info = update_transform_info M.apply_linear_velocity_ang_torque = apply_linear_velocity_ang_torque M.update_eligible_nodes = update_eligible_nodes M.apply_linear_velocity = apply_linear_velocity M.onExtensionLoaded = onExtensionLoaded + +M.set_collision = set_collision M.send_vehicle_config = send_vehicle_config return M diff --git a/KISSMultiplayer/scripts/kiss_mp/modScript.lua b/KISSMultiplayer/scripts/kiss_mp/modScript.lua index 6753543e..177e163d 100644 --- a/KISSMultiplayer/scripts/kiss_mp/modScript.lua +++ b/KISSMultiplayer/scripts/kiss_mp/modScript.lua @@ -2,6 +2,7 @@ print("Executing KissMP modScript...") loadJsonMaterialsFile("art/shapes/kissmp_playermodels/main.materials.json") setExtensionUnloadMode("kissplayers", "manual") +setExtensionUnloadMode("kissghosts", "manual") setExtensionUnloadMode("vehiclemanager", "manual") setExtensionUnloadMode("kisstransform", "manual") setExtensionUnloadMode("kissui", "manual") From 0bb9c18b9118feca5e4c93f1b809cbbd2d36173a Mon Sep 17 00:00:00 2001 From: DaddelZeit <141176220+DaddelZeit@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:46:17 +0200 Subject: [PATCH 2/6] Sync global ghost state --- .../lua/ge/extensions/kissghosts.lua | 24 ++++++++++++------- .../lua/ge/extensions/vehiclemanager.lua | 20 +++++++++++++--- kissmp-server/src/events.rs | 1 + kissmp-server/src/lua.rs | 4 +++- shared/src/vehicle/mod.rs | 1 + shared/src/vehicle/vehicle_meta.rs | 1 + 6 files changed, 38 insertions(+), 13 deletions(-) diff --git a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua index 377ca33c..b4632e46 100644 --- a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua +++ b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua @@ -1,14 +1,15 @@ local M = {} -local ghostSpawnsQueued = {} - M.veh_to_ghost_map = {} M.id_is_ghost = {} + +M.global_state = {} M.ghost_state = {} M.pause_overrides = {} local actual_ghost_states = {} -local function set_vehicle_ghost(veh_id, ghost_state, mesh_fade) +local function set_vehicle_ghost(veh_id, ghost_state, mesh_fade, is_global) + ghost_state = ghost_state or 0 if M.pause_overrides[veh_id] then -- we still want M.ghost_state to keep its original value ghost_state = 2 @@ -16,11 +17,14 @@ local function set_vehicle_ghost(veh_id, ghost_state, mesh_fade) M.ghost_state[veh_id] = ghost_state end + if is_global then + M.global_state[veh_id] = ghost_state + end + if actual_ghost_states[veh_id] == ghost_state then return end actual_ghost_states[veh_id] = ghost_state local veh = getObjectByID(veh_id) - if ghost_state == 0 then -- no ghost veh:setActive(1) veh:queueLuaCommand("kiss_vehicle.set_collision(true)") @@ -51,13 +55,13 @@ local function onUpdate() end end -local function set_pause_override(id, bool) +local function set_pause_override(id, bool, is_global) if not bool then M.pause_overrides[id] = nil else M.pause_overrides[id] = true end - set_vehicle_ghost(id, M.ghost_state[id], bool) + set_vehicle_ghost(id, M.ghost_state[id], bool, is_global) end local velocity = vec3() @@ -65,10 +69,11 @@ local function set_pause_override_for_owned(bool) for id in pairs(vehiclemanager.ownership) do local vehicle = getObjectByID(id) velocity:set(vehicle:getVelocityXYZ()) - if velocity:length() < 1 then - set_pause_override(id, bool) + if velocity:length() < 1 or not bool then + set_pause_override(id, bool, true) end end + vehiclemanager.send_vehicle_meta_updates() end local pause_counter = 0 @@ -82,6 +87,7 @@ local function attempt_pause(id) pause_counter = pause_counter + 1 if pause_counter > 0 then set_pause_override_for_owned(true) + SFXSystem.setGlobalParameter("g_GamePause", 1) end end @@ -94,12 +100,12 @@ local function attempt_unpause(id) if pause_counter == 0 then set_pause_override_for_owned(false) + SFXSystem.setGlobalParameter("g_GamePause", 0) end end local function onVehicleSpawned(vehId) if not network.connection.connected then return end - ghostSpawnsQueued[#ghostSpawnsQueued+1] = vehId end local function onVehicleDestroyed(vehId) diff --git a/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua b/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua index e5bc0b5b..a65f494f 100644 --- a/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua +++ b/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua @@ -7,6 +7,7 @@ local generation = 0 local meta_timer = 0 local colors_buffer = {} local plates_buffer = {} +local global_ghost_state_buffer = {} local first_vehicle = true M.loading_map = false @@ -115,12 +116,19 @@ local function send_vehicle_meta_updates() end colors_buffer[id] = colors + local ghost_state = kissghosts.global_state[id] or 0 + if global_ghost_state_buffer[id] then + changed = changed or global_ghost_state_buffer[id] ~= ghost_state + end + global_ghost_state_buffer[id] = ghost_state + if changed then local data = { VehicleMetaUpdate = { id, plate, - colors + colors, + ghost_state } } network.send_data(data, true) @@ -174,6 +182,7 @@ local function send_vehicle_config_inner(id, parts_config_json, buffer_data) vehicle_data.rotation = {rotation.x, rotation.y, rotation.z, rotation.w} vehicle_data.server_id = 0 vehicle_data.owner = 0 + vehicle_data.global_ghost_state = 0 network.send_data( { VehicleData = vehicle_data @@ -436,6 +445,8 @@ local function update_vehicle_meta(data) extensions.core_vehicle_manager.liveUpdateVehicleColors(id, vehicle, i, table_to_color(ct)) end vehicle:setField('partConfig', '', serialize(vd.config)) + + kissghosts.set_pause_override(id, data.global_ghost_state == 2, true) end local function electrics_diff_update(data) @@ -557,6 +568,10 @@ end local function onVehicleSpawned(id) if not network.connection.connected then return end local vehicle = getObjectByID(id) + vehicle:queueLuaCommand("extensions.addModulePath('lua/vehicle/extensions/kiss_mp')") + vehicle:queueLuaCommand("extensions.loadModulesInDirectory('lua/vehicle/extensions/kiss_mp')") + if vehicle.kissMpGhost then return end + tempVec1:set(vehicle:getPositionXYZ()) if first_vehicle then tempVec2:set(tempVec1.x + math.random(-5, 5), tempVec1.y + math.random(-5, 5), tempVec1.z) @@ -564,8 +579,6 @@ local function onVehicleSpawned(id) vehicle:queueLuaCommand("recovery.saveHome()") first_vehicle = false end - vehicle:queueLuaCommand("extensions.addModulePath('lua/vehicle/extensions/kiss_mp')") - vehicle:queueLuaCommand("extensions.loadModulesInDirectory('lua/vehicle/extensions/kiss_mp')") send_vehicle_config(id) -- Attempt to workaround a bug from latest beamng update. Also prevents unicycle cloning(Somewhat) if vehicle:getJBeamFilename() == "unicycle" then @@ -655,6 +668,7 @@ M.attach_coupler = attach_coupler M.detach_coupler = detach_coupler M.attach_coupler_inner = attach_coupler_inner M.detach_coupler_inner = detach_coupler_inner +M.send_vehicle_meta_updates = send_vehicle_meta_updates M.set_position = set_position M.set_position_rotation = set_position_rotation diff --git a/kissmp-server/src/events.rs b/kissmp-server/src/events.rs index d69f3908..dbca766f 100644 --- a/kissmp-server/src/events.rs +++ b/kissmp-server/src/events.rs @@ -192,6 +192,7 @@ impl Server { vehicle.data.palete_0 = meta.colors_table[1]; vehicle.data.palete_1 = meta.colors_table[2]; vehicle.data.plate = meta.plate.clone(); + vehicle.data.global_ghost_state = meta.global_ghost_state.clone(); let mut meta = meta.clone(); meta.vehicle_id = server_id; for (_, client) in &mut self.connections { diff --git a/kissmp-server/src/lua.rs b/kissmp-server/src/lua.rs index 2ce25d2b..1ffcbc02 100644 --- a/kissmp-server/src/lua.rs +++ b/kissmp-server/src/lua.rs @@ -426,7 +426,7 @@ pub fn setup_lua() -> (rlua::Lua, mpsc::Receiver) { let build_vehicle = lua_ctx .create_function( move |_, - (parts_config, color, p0, p1, plate, name, position, rotation): ( + (parts_config, color, p0, p1, plate, name, position, rotation, global_ghost_state): ( String, Vec, Vec, @@ -435,6 +435,7 @@ pub fn setup_lua() -> (rlua::Lua, mpsc::Receiver) { String, Vec, Vec, + i8, )| { Ok(LuaVehicleData(VehicleData { parts_config, @@ -448,6 +449,7 @@ pub fn setup_lua() -> (rlua::Lua, mpsc::Receiver) { owner: None, position: [position[0], position[1], position[2]], rotation: [rotation[0], rotation[1], rotation[2], rotation[3]], + global_ghost_state: global_ghost_state, })) }, ) diff --git a/shared/src/vehicle/mod.rs b/shared/src/vehicle/mod.rs index 73d22fef..45c4c596 100644 --- a/shared/src/vehicle/mod.rs +++ b/shared/src/vehicle/mod.rs @@ -30,6 +30,7 @@ pub struct VehicleData { pub owner: Option, pub position: [f32; 3], pub rotation: [f32; 4], + pub global_ghost_state: i8, } // A single packet that contains all of the vehicle updates. diff --git a/shared/src/vehicle/vehicle_meta.rs b/shared/src/vehicle/vehicle_meta.rs index f80f94a2..6f0b0e3a 100644 --- a/shared/src/vehicle/vehicle_meta.rs +++ b/shared/src/vehicle/vehicle_meta.rs @@ -5,4 +5,5 @@ pub struct VehicleMeta { pub vehicle_id: u32, pub plate: Option, pub colors_table: [[f32; 8]; 3], + pub global_ghost_state: i8, } From eed0f9640a1560a6221ddc4d8cd3bd224cae5e8e Mon Sep 17 00:00:00 2001 From: DaddelZeit <141176220+DaddelZeit@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:46:52 +0200 Subject: [PATCH 3/6] Block pausing and slowmotion inputs, reroute UI pause requests to kissghosts --- KISSMultiplayer/lua/ge/extensions/network.lua | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/KISSMultiplayer/lua/ge/extensions/network.lua b/KISSMultiplayer/lua/ge/extensions/network.lua index e31bd5a4..7a19e317 100644 --- a/KISSMultiplayer/lua/ge/extensions/network.lua +++ b/KISSMultiplayer/lua/ge/extensions/network.lua @@ -31,6 +31,16 @@ M.connection = { time_offset = 0 } +local original_simTimeAuthorityFuncs = nil +-- pushPauseRequest +-- popPauseRequest + +local blocked_inputs = {"slower_motion", "faster_motion", "toggle_slow_motion", "pause"} +local function block_inputs(block) + core_input_actionFilter.setGroup('kissmpActions', blocked_inputs) + core_input_actionFilter.addAction(0, 'kissmpActions', block) +end + local CHUNK_SIZE = 65000 -- Safe size under 65536 limit local message_handlers = {} @@ -121,6 +131,12 @@ local function disconnect(data) if getMissionFilename() ~= "" then returnToMainMenu() end + + if original_simTimeAuthorityFuncs then + simTimeAuthority.pushPauseRequest = original_simTimeAuthorityFuncs.pushPauseRequest + simTimeAuthority.popPauseRequest = original_simTimeAuthorityFuncs.popPauseRequest + end + block_inputs(false) end local function handle_player_info(player_info) @@ -395,10 +411,20 @@ local function connect(addr, player_name, is_public) end end vehiclemanager.loading_map = true + + original_simTimeAuthorityFuncs = { + pushPauseRequest = simTimeAuthority.pushPauseRequest, + popPauseRequest = simTimeAuthority.popPauseRequest, + } + + simTimeAuthority.pushPauseRequest = kissghosts.attempt_pause + simTimeAuthority.popPauseRequest = kissghosts.attempt_unpause + if #missing_mods == 0 then kissmods.mount_mods(mod_names) change_map(server_info.map) end + block_inputs(true) kissrichpresence.update() kissui.chat.add_message("Connected!") end From 2db3c51857e5b6ad8e24c469799c5580e7b2f02a Mon Sep 17 00:00:00 2001 From: DaddelZeit <141176220+DaddelZeit@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:54:21 +0200 Subject: [PATCH 4/6] Clean unused stuff, proper cleanup --- KISSMultiplayer/lua/ge/extensions/kissghosts.lua | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua index b4632e46..23aaa5af 100644 --- a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua +++ b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua @@ -1,8 +1,5 @@ local M = {} -M.veh_to_ghost_map = {} -M.id_is_ghost = {} - M.global_state = {} M.ghost_state = {} M.pause_overrides = {} @@ -104,20 +101,13 @@ local function attempt_unpause(id) end end -local function onVehicleSpawned(vehId) - if not network.connection.connected then return end -end - local function onVehicleDestroyed(vehId) + M.global_state[vehId] = nil + M.ghost_state[vehId] = nil M.pause_overrides[vehId] = nil end -local function onExtensionLoaded() -end - M.onUpdate = onUpdate -M.onExtensionLoaded = onExtensionLoaded -M.onVehicleSpawned = onVehicleSpawned M.onVehicleDestroyed = onVehicleDestroyed M.set_vehicle_ghost = set_vehicle_ghost From 0917820943ee13fc0ec5ee6f4b363133bc486785 Mon Sep 17 00:00:00 2001 From: DaddelZeit <141176220+DaddelZeit@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:40:12 +0200 Subject: [PATCH 5/6] Go into ghost mode on reset/don't go out of ghost mode when exiting pause (if inside another bounding box) --- .../lua/ge/extensions/kissghosts.lua | 116 ++++++++++++++---- .../lua/ge/extensions/vehiclemanager.lua | 9 +- 2 files changed, 94 insertions(+), 31 deletions(-) diff --git a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua index 23aaa5af..db515077 100644 --- a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua +++ b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua @@ -2,14 +2,14 @@ local M = {} M.global_state = {} M.ghost_state = {} -M.pause_overrides = {} +M.overrides = {} local actual_ghost_states = {} local function set_vehicle_ghost(veh_id, ghost_state, mesh_fade, is_global) ghost_state = ghost_state or 0 - if M.pause_overrides[veh_id] then + if M.overrides[veh_id] then -- we still want M.ghost_state to keep its original value - ghost_state = 2 + ghost_state = M.overrides[veh_id] else M.ghost_state[veh_id] = ghost_state end @@ -35,44 +35,97 @@ local function set_vehicle_ghost(veh_id, ghost_state, mesh_fade, is_global) veh:setActive(0) end - if type(mesh_fade) ~= "number" then - mesh_fade = mesh_fade and 0.5 or 1 + if mesh_fade ~= nil then + if type(mesh_fade) ~= "number" then + mesh_fade = mesh_fade and 0.5 or 1 + end + veh:setMeshAlpha(mesh_fade, "") end - veh:setMeshAlpha(mesh_fade, "") end -local function onUpdate() - for id, v in pairs(M.pause_overrides) do - -- 0.75 appears to be a safe value - -- values too small cause breakage - local veh = getObjectByID(id) - if veh and v then - veh:applyClusterVelocityScaleAdd(0, 0.75, 0, 0, 0) +local bb_center_a, bb_half_axis0_a, bb_half_axis1_a, bb_half_axis2_a = vec3(), vec3(), vec3(), vec3() +local bb_center_b, bb_half_axis0_b, bb_half_axis1_b, bb_half_axis2_b = vec3(), vec3(), vec3(), vec3() + +local function check_overlaps(vid_a) + bb_center_a:set(be:getObjectOOBBCenterXYZ(vid_a)) + bb_half_axis0_a:set(be:getObjectOOBBHalfAxisXYZ(vid_a, 0)) + bb_half_axis1_a:set(be:getObjectOOBBHalfAxisXYZ(vid_a, 1)) + bb_half_axis2_a:set(be:getObjectOOBBHalfAxisXYZ(vid_a, 2)) + + for vid_b in vehiclesIterator() do + if vid_a ~= vid_b and not vehiclemanager.ownership[vid_b] then + bb_center_b:set(be:getObjectOOBBCenterXYZ(vid_b)) + bb_half_axis0_b:set(be:getObjectOOBBHalfAxisXYZ(vid_b, 0)) + bb_half_axis1_b:set(be:getObjectOOBBHalfAxisXYZ(vid_b, 1)) + bb_half_axis2_b:set(be:getObjectOOBBHalfAxisXYZ(vid_b, 2)) + + if overlapsOBB_OBB(bb_center_a, bb_half_axis0_a, bb_half_axis1_a, bb_half_axis2_a, + bb_center_b, bb_half_axis0_b, bb_half_axis1_b, bb_half_axis2_b) then + return true + end end end + + return false +end + +local function set_respawn_override(vid, is_global) + if check_overlaps(vid) then + M.overrides[vid] = 1 + else + M.overrides[vid] = nil + end + + set_vehicle_ghost(vid, M.ghost_state[vid], M.overrides[vid] ~= nil, is_global) end -local function set_pause_override(id, bool, is_global) - if not bool then - M.pause_overrides[id] = nil +local function set_pause_override(vid, override, is_global) + if override then + M.overrides[vid] = 2 + elseif check_overlaps(vid) then + M.overrides[vid] = 1 else - M.pause_overrides[id] = true + M.overrides[vid] = nil end - set_vehicle_ghost(id, M.ghost_state[id], bool, is_global) + + set_vehicle_ghost(vid, M.ghost_state[vid], M.overrides[vid] ~= nil, is_global) end local velocity = vec3() local function set_pause_override_for_owned(bool) for id in pairs(vehiclemanager.ownership) do - local vehicle = getObjectByID(id) - velocity:set(vehicle:getVelocityXYZ()) - if velocity:length() < 1 or not bool then - set_pause_override(id, bool, true) + if bool then + local vehicle = getObjectByID(id) + velocity:set(vehicle:getVelocityXYZ()) + if velocity:length() < 1 then + set_pause_override(id, true, true) + end + else + set_pause_override(id, false, true) end end vehiclemanager.send_vehicle_meta_updates() end +local function onUpdate() + for vid, v in pairs(actual_ghost_states) do + if v == 2 then + -- 0.75 appears to be a safe value + -- values too small cause breakage + local veh = getObjectByID(vid) + if veh and v then + veh:applyClusterVelocityScaleAdd(0, 0.75, 0, 0, 0) + end + end + end + + for vid, v in pairs(M.overrides) do + if v == 1 then + set_respawn_override(vid, true) + end + end +end + local pause_counter = 0 local pause_requests = {} @@ -101,17 +154,34 @@ local function attempt_unpause(id) end end +local function onVehicleSpawned(veh_id) + -- because collisions are disabled vehicle side on respawn, this *must* update + actual_ghost_states[veh_id] = nil + + set_respawn_override(veh_id, true) +end + +local function onVehicleResetted(veh_id) + -- because collisions are disabled vehicle side on respawn, this *must* update + actual_ghost_states[veh_id] = nil + + set_respawn_override(veh_id, true) +end + local function onVehicleDestroyed(vehId) M.global_state[vehId] = nil M.ghost_state[vehId] = nil - M.pause_overrides[vehId] = nil + M.overrides[vehId] = nil end M.onUpdate = onUpdate M.onVehicleDestroyed = onVehicleDestroyed +M.onVehicleSpawned = onVehicleSpawned +M.onVehicleResetted = onVehicleResetted M.set_vehicle_ghost = set_vehicle_ghost M.set_pause_override = set_pause_override +M.set_respawn_override = set_respawn_override M.attempt_pause = attempt_pause M.attempt_unpause = attempt_unpause diff --git a/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua b/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua index a65f494f..a18fc60f 100644 --- a/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua +++ b/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua @@ -570,15 +570,8 @@ local function onVehicleSpawned(id) local vehicle = getObjectByID(id) vehicle:queueLuaCommand("extensions.addModulePath('lua/vehicle/extensions/kiss_mp')") vehicle:queueLuaCommand("extensions.loadModulesInDirectory('lua/vehicle/extensions/kiss_mp')") - if vehicle.kissMpGhost then return end + vehicle:queueLuaCommand("rawset(_G, 'ghostOnReset', true)") -- this is important for ghosting - tempVec1:set(vehicle:getPositionXYZ()) - if first_vehicle then - tempVec2:set(tempVec1.x + math.random(-5, 5), tempVec1.y + math.random(-5, 5), tempVec1.z) - vehicle:setPosition(tempVec2) - vehicle:queueLuaCommand("recovery.saveHome()") - first_vehicle = false - end send_vehicle_config(id) -- Attempt to workaround a bug from latest beamng update. Also prevents unicycle cloning(Somewhat) if vehicle:getJBeamFilename() == "unicycle" then From 471dd9ae1621a6651465ff5eb60c18ba8a02b8c5 Mon Sep 17 00:00:00 2001 From: DaddelZeit <141176220+DaddelZeit@users.noreply.github.com> Date: Sat, 11 Apr 2026 00:28:14 +0200 Subject: [PATCH 6/6] Don't check for overlaps between vehicles of the same player --- KISSMultiplayer/lua/ge/extensions/kissghosts.lua | 3 ++- KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua index db515077..785354e4 100644 --- a/KISSMultiplayer/lua/ge/extensions/kissghosts.lua +++ b/KISSMultiplayer/lua/ge/extensions/kissghosts.lua @@ -52,8 +52,9 @@ local function check_overlaps(vid_a) bb_half_axis1_a:set(be:getObjectOOBBHalfAxisXYZ(vid_a, 1)) bb_half_axis2_a:set(be:getObjectOOBBHalfAxisXYZ(vid_a, 2)) + local id_to_owner_map = vehiclemanager.id_to_owner_map for vid_b in vehiclesIterator() do - if vid_a ~= vid_b and not vehiclemanager.ownership[vid_b] then + if vid_a ~= vid_b and id_to_owner_map[vid_a] ~= id_to_owner_map[vid_b] then bb_center_b:set(be:getObjectOOBBCenterXYZ(vid_b)) bb_half_axis0_b:set(be:getObjectOOBBHalfAxisXYZ(vid_b, 0)) bb_half_axis1_b:set(be:getObjectOOBBHalfAxisXYZ(vid_b, 1)) diff --git a/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua b/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua index a18fc60f..a1ebd486 100644 --- a/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua +++ b/KISSMultiplayer/lua/ge/extensions/vehiclemanager.lua @@ -14,6 +14,7 @@ M.loading_map = false M.id_map = {} M.server_ids = {} M.ownership = {} +M.id_to_owner_map = {} M.vehicle_updates_buffer = {} M.packet_gen_buffer = {} M.is_network_session = false @@ -224,6 +225,7 @@ local function spawn_vehicle(data) M.vehicle_buffer[data.server_id] = data return end + M.id_to_owner_map[data.in_game_id] = data.owner if data.owner == network.get_client_id() then log("I", "kissmp.vehiclemanager.spawn_vehicle", "Vehicle belongs to local client, setting ownership") M.id_map[data.server_id] = data.in_game_id @@ -634,6 +636,7 @@ local function onMissionLoaded(mission) if not network.connection.connected then return end M.id_map = {} M.ownership = {} + M.id_to_owner_map = {} M.loading_map = false first_vehicle = true end