voiceData = {} radioData = {} callData = {} local mappedChannels = {} function firstFreeChannel() for i = 1, 2048 do if not mappedChannels[i] then return i end end return 0 end function defaultTable(source) handleStateBagInitilization(source) return { radio = 0, call = 0, lastRadio = 0, lastCall = 0 } end function handleStateBagInitilization(source) local plyState = Player(source).state if not plyState.pmaVoiceInit then plyState:set('radio', GetConvarInt('voice_defaultRadioVolume', 30), true) plyState:set('call', GetConvarInt('voice_defaultCallVolume', 60), true) plyState:set('submix', nil, true) plyState:set('proximity', {}, true) plyState:set('callChannel', 0, true) plyState:set('radioChannel', 0, true) plyState:set('voiceIntent', 'speech', true) -- We want to save voice inits because we'll automatically reinitalize calls and channels plyState:set('pmaVoiceInit', true, false) end local assignedChannel = firstFreeChannel() plyState:set('assignedChannel', assignedChannel, true) if assignedChannel ~= 0 then mappedChannels[assignedChannel] = source logger.verbose('[reuse] Assigned %s to channel %s', source, assignedChannel) else logger.error('[reuse] Failed to find a free channel for %s', source) end end CreateThread(function() local plyTbl = GetPlayers() for i = 1, #plyTbl do local ply = tonumber(plyTbl[i]) voiceData[ply] = defaultTable(plyTbl[i]) end Wait(5000) local nativeAudio = GetConvar('voice_useNativeAudio', 'not-set') local _3dAudio = GetConvar('voice_use3dAudio', 'not-set') local _2dAudio = GetConvar('voice_use2dAudio', 'not-set') local sendingRangeOnly = GetConvar('voice_useSendingRangeOnly', 'not-set') local gameVersion = GetConvar('gamename', 'fivem') -- handle no convars being set (default drag n' drop) if nativeAudio == 'not-set' and _3dAudio == 'not-set' and _2dAudio == 'not-set' then SetConvarReplicated('voice_useNativeAudio', 'true') if sendingRangeOnly == 'not-set' then SetConvarReplicated('voice_useSendingRangeOnly', 'true') logger.info( 'No convars detected for voice mode, defaulting to \'setr voice_useNativeAudio true\' and \'setr voice_useSendingRangeOnly true\'') else logger.info('No voice mod detected, defaulting to \'setr voice_useNativeAudio true\'') end elseif sendingRangeOnly == 'not-set' then logger.warn( "It's recommended to have 'voice_useSendingRangeOnly' set to true, you can do that with 'setr voice_useSendingRangeOnly true', this prevents players who directly join the mumble server from broadcasting to players.") end local radioVolume = GetConvarInt("voice_defaultRadioVolume", 30) local callVolume = GetConvarInt("voice_defaultCallVolume", 60) -- When casted to an integer these get set to 0 or 1, so warn on these values that they don't work if radioVolume == 0 or radioVolume == 1 or callVolume == 0 or callVolume == 1 then SetConvarReplicated("voice_defaultRadioVolume", 30) SetConvarReplicated("voice_defaultCallVolume", 60) for i = 1, 5 do Wait(5000) logger.warn( "`voice_defaultRadioVolume` or `voice_defaultCallVolume` have their value set as a float, this is going to automatically be fixed but please update your convars.") end end end) AddEventHandler('playerJoining', function() if not voiceData[source] then voiceData[source] = defaultTable(source) end end) AddEventHandler("playerDropped", function() local source = source local mappedChannel = Player(source).state.assignedChannel if voiceData[source] then local plyData = voiceData[source] if plyData.radio ~= 0 then removePlayerFromRadio(source, plyData.radio) end if plyData.call ~= 0 then removePlayerFromCall(source, plyData.call) end voiceData[source] = nil end if mappedChannel then mappedChannels[mappedChannel] = nil logger.verbose('[reuse] Unassigned %s from channel %s', source, mappedChannel) end end) if GetConvarInt('voice_externalDisallowJoin', 0) == 1 then AddEventHandler('playerConnecting', function(_, _, deferral) deferral.defer() Wait(0) deferral.done('This server is not accepting connections.') end) end -- only meant for internal use so no documentation function isValidPlayer(source) return voiceData[source] end exports('isValidPlayer', isValidPlayer) function getPlayersInRadioChannel(channel) local returnChannel = radioData[channel] if returnChannel then return returnChannel end -- channel doesnt exist return {} end exports('getPlayersInRadioChannel', getPlayersInRadioChannel) exports('GetPlayersInRadioChannel', getPlayersInRadioChannel)