2025-04-07 01:41:12 +00:00

254 lines
7.6 KiB
Lua

-- used when muted
local disableUpdates = false
local isListenerEnabled = false
local plyCoords = GetEntityCoords(PlayerPedId())
proximity = MumbleGetTalkerProximity()
currentTargets = {}
-- a list of all the players the current client us listening to
-- the value will be set to false if we didn't actually start listening to them (in situations where their channel didn't exist)
-- TODO: PR a native to let us get if we're listening to a certain channel.
local listeners = {}
function orig_addProximityCheck(ply)
local tgtPed = GetPlayerPed(ply)
local voiceRange = GetConvar('voice_useNativeAudio', 'false') == 'true' and proximity * 3 or proximity
local distance = #(plyCoords - GetEntityCoords(tgtPed))
return distance < voiceRange, distance
end
local addProximityCheck = orig_addProximityCheck
exports("overrideProximityCheck", function(fn)
addProximityCheck = fn
end)
exports("resetProximityCheck", function()
addProximityCheck = orig_addProximityCheck
end)
function addNearbyPlayers()
if disableUpdates then return end
-- update here so we don't have to update every call of addProximityCheck
plyCoords = GetEntityCoords(PlayerPedId())
proximity = MumbleGetTalkerProximity()
currentTargets = {}
MumbleClearVoiceTargetChannels(voiceTarget)
if LocalPlayer.state.disableProximity then return end
MumbleAddVoiceChannelListen(LocalPlayer.state.assignedChannel)
MumbleAddVoiceTargetChannel(voiceTarget, LocalPlayer.state.assignedChannel)
for source, _ in pairs(callData) do
if source ~= playerServerId then
local channel = MumbleGetVoiceChannelFromServerId(source)
if channel ~= -1 then
MumbleAddVoiceTargetChannel(voiceTarget, channel)
end
end
end
local players = GetActivePlayers()
for i = 1, #players do
local ply = players[i]
local serverId = GetPlayerServerId(ply)
local shouldAdd, distance = addProximityCheck(ply)
if shouldAdd then
-- if distance then
-- currentTargets[serverId] = distance
-- else
-- -- backwards compat, maybe remove in v7
-- currentTargets[serverId] = 15.0
-- end
-- logger.verbose('Added %s as a voice target', serverId)
local channel = MumbleGetVoiceChannelFromServerId(serverId)
if channel ~= -1 then
MumbleAddVoiceTargetChannel(voiceTarget, channel)
end
end
end
end
function addChannelListener(serverId)
-- not in the documentation, but this will return -1 whenever the client isn't in a channel
local channel = MumbleGetVoiceChannelFromServerId(serverId)
if channel ~= -1 then
MumbleAddVoiceChannelListen(channel)
logger.verbose("Adding %s to listen table", serverId)
end
listeners[serverId] = channel ~= -1
end
function removeChannelListener(serverId)
if listeners[serverId] then
local channel = MumbleGetVoiceChannelFromServerId(serverId)
if channel ~= -1 then
MumbleRemoveVoiceChannelListen(channel)
end
logger.verbose("Removing %s from listen table", serverId)
end
-- remove the listener if they exist
listeners[serverId] = nil
end
function setSpectatorMode(enabled)
logger.info('Setting spectate mode to %s', enabled)
isListenerEnabled = enabled
local players = GetActivePlayers()
if isListenerEnabled then
for i = 1, #players do
local ply = players[i]
local serverId = GetPlayerServerId(ply)
if serverId == playerServerId then goto skip_loop end
addChannelListener(serverId)
::skip_loop::
end
else
for i = 1, #players do
local ply = players[i]
local serverId = GetPlayerServerId(ply)
if serverId == playerServerId then goto skip_loop end
removeChannelListener(serverId)
::skip_loop::
end
-- cleanup table if we stop listening
listeners = {}
end
end
function tryListeningToFailedListeners()
for src, isListening in pairs(listeners) do
-- if we failed to listen before we'll be set to false
if not isListening then
addChannelListener(src)
end
end
end
RegisterNetEvent('onPlayerJoining', function(serverId)
if isListenerEnabled then
addChannelListener(serverId)
end
end)
RegisterNetEvent('onPlayerDropped', function(serverId)
if isListenerEnabled then
removeChannelListener(serverId)
end
end)
local listenerOverride = false
exports("setListenerOverride", function(enabled)
type_check({ enabled, "boolean" })
listenerOverride = enabled
end)
-- cache talking status so we only send a nui message when its not the same as what it was before
local lastTalkingStatus = false
local lastRadioStatus = false
local voiceState = "proximity"
CreateThread(function()
TriggerEvent('chat:addSuggestion', '/muteply', 'Mutes the player with the specified id', {
{ name = "player id", help = "the player to toggle mute" },
{ name = "duration", help = "(opt) the duration the mute in seconds (default: 900)" }
})
while true do
-- wait for mumble to reconnect
while not MumbleIsConnected() or not isInitialized do
Wait(100)
end
-- Leave the check here as we don't want to do any of this logic
if GetConvarInt('voice_enableUi', 1) == 1 then
local curTalkingStatus = MumbleIsPlayerTalking(PlayerId()) == 1
if lastRadioStatus ~= radioPressed or lastTalkingStatus ~= curTalkingStatus then
lastRadioStatus = radioPressed
lastTalkingStatus = curTalkingStatus
sendUIMessage({
usingRadio = lastRadioStatus,
talking = lastTalkingStatus
})
end
end
if voiceState == "proximity" then
addNearbyPlayers()
-- What a name, wowza
local cam = GetConvarInt("voice_disableAutomaticListenerOnCamera", 0) ~= 1 and GetRenderingCam() or -1
local isSpectating = NetworkIsInSpectatorMode() or cam ~= -1
if not isListenerEnabled and (isSpectating or listenerOverride) then
setSpectatorMode(true)
elseif isListenerEnabled and not isSpectating and not listenerOverride then
setSpectatorMode(false)
end
tryListeningToFailedListeners()
end
Wait(GetConvarInt('voice_refreshRate', 200))
end
end)
exports("setVoiceState", function(_voiceState, channel)
if _voiceState ~= "proximity" and _voiceState ~= "channel" then
logger.error("Didn't get a proper voice state, expected proximity or channel, got %s", _voiceState)
end
voiceState = _voiceState
if voiceState == "channel" then
type_check({ channel, "number" })
-- 65535 is the highest a client id can go, so we add that to the base channel so we don't manage to get onto a players channel
channel = channel + 65535
MumbleSetVoiceChannel(channel)
while MumbleGetVoiceChannelFromServerId(playerServerId) ~= channel do
Wait(250)
end
MumbleAddVoiceTargetChannel(voiceTarget, channel)
elseif voiceState == "proximity" then
handleInitialState()
end
end)
AddEventHandler("onClientResourceStop", function(resource)
if type(addProximityCheck) == "table" then
local proximityCheckRef = addProximityCheck.__cfx_functionReference
if proximityCheckRef then
local isResource = string.match(proximityCheckRef, resource)
if isResource then
addProximityCheck = orig_addProximityCheck
logger.warn(
'Reset proximity check to default, the original resource [%s] which provided the function restarted',
resource)
end
end
end
end)
exports("addVoiceMode", function(distance, name)
for i = 1, #Cfg.voiceModes do
local voiceMode = Cfg.voiceModes[i]
if voiceMode[2] == name then
logger.verbose("Already had %s, overwritting instead", name)
voiceMode[1] = distance
return
end
end
Cfg.voiceModes[#Cfg.voiceModes + 1] = { distance, name }
end)
exports("removeVoiceMode", function(name)
for i = 1, #Cfg.voiceModes do
local voiceMode = Cfg.voiceModes[i]
if voiceMode[2] == name then
table.remove(Cfg.voiceModes, i)
-- Reset our current range if we had it
if mode == i then
local newMode = Cfg.voiceModes[1]
mode = 1
setProximityState(newMode[mode], false)
end
return true
end
end
return false
end)