Need Assistance with creating spawnpoints off of discord roles

Hey everyone,

Looking for some assistance with modifying our spawning structure to allow for specific spawn points based on a players discord roles. I have an FXServer, running FiveM and FivePD.

For example, if a player has been assigned to the LSPD in our discord, when they entered the server, they would spawn at the designated spawn point for LSPD officers. Same for the Sheriff’s Department as they would spawn at their Sheriff’s Department HQ, and the same with highway patrol.

We currently implement Badger’s Discord API and Discord Ace Perms. We have amended our spawnmanager.lua to reflect the desired spawnpoints, we have set our roles in discordaceperms, and when we attempt to do this, the player WILL spawn at one of the selected spawns within the spawnmanager.lua file, however, its not consistent with the designated role. Essentially, if you are with the Sheriff’s Department- you may spawn at the Sheriff’s Department HQ, but you can just as equally spawn at LSPD or the LSFD designated spawn points.

Its like, the discord roles are being recognized, but not consistently. And the spawn points have been read correctly, just not everytime.

I noticed that when I change our Map.lua file within the fivem-hipster map directory and add the spawn points (instead of just having one at MRPD), this randomized spawn point changing happens… So I am not sure if I need to make a script to add to the map.lua file? or if I need to change something else?

If anyone can help me work on this? Its possible, just need help with scripting the changes and where to put it.

Hey, i have no knowlegde of FivePD, but i do of Badgers Discord API.

Try this:
Client.lua

AddEventHandler('playerSpawned', function()
	TriggerServerEvent("DiscordRoleCheck:sv")
end)

server.lua

RegisterNetEvent('DiscordRoleCheck:sv', function()
	local src = source
	local roleIDs = exports.Badger_Discord_API:GetDiscordRoles(src)
	if not (roleIDs == false) then
		for i = 1, #roleIDs do
			if exports.Badger_Discord_API:CheckEqual("LSPD", roleIDs[i]) then
				SetEntityCoords(GetPlayerPed(src),vector3(426.1223, -979.7187, 30.70979))
			elseif exports.Badger_Discord_API:CheckEqual("SandyPD", roleIDs[i]) then
				SetEntityCoords(GetPlayerPed(src),vector3(1857.092, 3679.564, 33.78053))
			end
		end
	end
end)

Hope it works, or else let me know :smiley:

1 Like

First of all, Thank you for responding! Its been hard to get any guidance with this. I am trying to learn. I dont ask anyone to just do it for me, but I am still getting left out in the field, lol.

Here is my client.lua file (which is a little different than yours):

triggered = false;
AddEventHandler("playerSpawned", function()
    if not triggered then 
        triggered = true;
        Citizen.Wait((1000 * 20)); -- Wait 20 seconds
        TriggerServerEvent('Badger_Discord_API:PlayerLoaded');
    end
end)

I am confused on why the triggered is equal to false at first?

here is my server.lua:

local FormattedToken = "Bot " .. Config.Bot_Token

local error_codes_defined = {
	[200] = 'OK - The request was completed successfully..!',
	[400] = "Error - The request was improperly formatted, or the server couldn't understand it..!",
	[401] = 'Error - The Authorization header was missing or invalid..! Your Discord Token is probably wrong or does not have correct permissions attributed to it.',
	[403] = 'Error - The Authorization token you passed did not have permission to the resource..! Your Discord Token is probably wrong or does not have correct permissions attributed to it.',
	[404] = "Error - The resource at the location specified doesn't exist.",
	[429] = 'Error - Too many requests, you hit the Discord rate limit. https://discord.com/developers/docs/topics/rate-limits',
	[502] = 'Error - Discord API may be down?...'
};

Citizen.CreateThread(function()
	if (GetCurrentResourceName() ~= "Badger_Discord_API") then 
		StopResource(GetCurrentResourceName());
		print("[" .. GetCurrentResourceName() .. "] " .. "IMPORTANT: This resource must be named Badger_Discord_API for it to work properly with other scripts...");
	end
	print("[Badger_Discord_API] For support, make sure to join Badger's official Discord server: discord.gg/WjB5VFz");
	print('^7[^2Zap-Hosting^7] ^3Use code ^5TheWolfBadger-4765 ^3at checkout for ^220% ^3off of selected services. Visit ^5https://zap-hosting.com/badger ^3to get started!');
end)

tracked = {}

RegisterNetEvent('Badger_Discord_API:PlayerLoaded')
AddEventHandler('Badger_Discord_API:PlayerLoaded', function()
	if (GetCurrentResourceName() ~= "Badger_Discord_API") then 
		TriggerClientEvent('chatMessage', -1, '^1[^5SCRIPT ERROR^1] ^3The script ^1' .. GetCurrentResourceName() .. ' ^3will not work properly... You must '
	.. 'rename the resource to ^1Badger_Discord_API');
	end
	local license = GetIdentifier(source, 'license');
	if (tracked[license] == nil) then 
		tracked[license] = true;
		--TriggerClientEvent('chatMessage', source, 
		--'^1[^5Badger_Discord_API^1] ^3The Discord API script was created by Badger. You may join his Discord at: ^6discord.gg/WjB5VFz')
	end
	--TriggerClientEvent('chatMessage', source, 
		--'^7[^2Zap-Hosting^7] ^3Use code ^5TheWolfBadger-4765 ^3at checkout for ^220% ^3off of selected services. Visit ^5https://zap-hosting.com/badger ^3to get started!');
end)

card = '{"type":"AdaptiveCard","$schema":"http://adaptivecards.io/schemas/adaptive-card.json","version":"1.3","body":[{"type":"Image","url":"' .. Config.Splash.Header_IMG .. '","horizontalAlignment":"Center"},{"type":"Container","items":[{"type":"TextBlock","text":"Badger_Discord_API","wrap":true,"fontType":"Default","size":"ExtraLarge","weight":"Bolder","color":"Light","horizontalAlignment":"Center"},{"type":"TextBlock","text":"' .. Config.Splash.Heading1 .. '","wrap":true,"size":"Large","weight":"Bolder","color":"Light","horizontalAlignment":"Center"},{"type":"TextBlock","text":"' .. Config.Splash.Heading2 .. '","wrap":true,"color":"Light","size":"Medium","horizontalAlignment":"Center"},{"type":"ColumnSet","height":"stretch","minHeight":"100px","bleed":true,"horizontalAlignment":"Center","columns":[{"type":"Column","width":"stretch","items":[{"type":"ActionSet","actions":[{"type":"Action.OpenUrl","title":"Discord","url":"' .. Config.Splash.Discord_Link .. '","style":"positive"}]}],"height":"stretch"},{"type":"Column","width":"stretch","items":[{"type":"ActionSet","actions":[{"type":"Action.OpenUrl","title":"Website","style":"positive","url":"' .. Config.Splash.Website_Link .. '"}]}]}]},{"type":"ActionSet","actions":[{"type":"Action.OpenUrl","title":"Click to join Badger\'s Discord","style":"destructive","iconUrl":"https://i.gyazo.com/0904b936e8e30d0104dec44924bd2294.gif","url":"https://discord.com/invite/WjB5VFz"}]}],"style":"default","bleed":true,"height":"stretch"},{"type":"Image","url":"https://i.gyazo.com/7e896862b14be754ae8bad90b664a350.png","selectAction":{"type":"Action.OpenUrl","url":"https://zap-hosting.com/badger"},"horizontalAlignment":"Center"}]}'
if Config.Splash.Enabled then 
	AddEventHandler('playerConnecting', function(name, setKickReason, deferrals) 
		-- Player is connecting
		deferrals.defer();
		local src = source;
		local toEnd = false;
		local count = 0;
		while not toEnd do 
			deferrals.presentCard(card,
			function(data, rawData)
			end)
			Wait((1000))
			count = count + 1;
			if count == Config.Splash.Wait then 
				toEnd = true;
			end
		end
		deferrals.done();
	end)
end 

function GetIdentifier(source, id_type)
    if type(id_type) ~= "string" then return print('Invalid usage') end
    for _, identifier in pairs(GetPlayerIdentifiers(source)) do
        if string.find(identifier, id_type) then
            return identifier
        end
    end
    return nil
end

function GetGuildId (guildName)
  local result = Config.Guild_ID
  if guildName and Config.Guilds[guildName] then
    result = tostring(Config.Guilds[guildName])
  end
  return result
end

function DiscordRequest(method, endpoint, jsondata)
    local data = nil
    PerformHttpRequest("https://discordapp.com/api/"..endpoint, function(errorCode, resultData, resultHeaders)
		data = {data=resultData, code=errorCode, headers=resultHeaders}
    end, method, #jsondata > 0 and json.encode(jsondata) or "", {["Content-Type"] = "application/json", ["Authorization"] = FormattedToken})

    while data == nil do
        Citizen.Wait(0)
    end
	
    return data
end

function GetRoleIdFromRoleName(name, guild --[[optional]])
  local guildId = GetGuildId(guild)
	if (Caches.RoleList[guildId] ~= nil) then 
		return tonumber(Caches.RoleList[name]);
	else 
		local roles = GetGuildRoleList(guild);
		return tonumber(roles[name]);
	end
end

function CheckEqual(role1, role2, guild --[[optional]])
	local checkStr1 = false;
	local checkStr2 = false;
	local roleID1 = role1;
	local roleID2 = role2;
	local searchGuild1 = true;
	local searchGuild2 = true;
	if type(role1) == "string" then checkStr1 = true end;
	if type(role2) == "string" then checkStr2 = true end; 
	if checkStr1 then 
		local roles2 = Config.RoleList;
		for roleRef, roleID in pairs(roles2) do 
			if roleRef == role1 then 
				roleID1 = roleID;
				searchGuild1 = false;
			end
		end
		if searchGuild1 then 
			local roles = GetGuildRoleList(guild);
			for roleName, roleID in pairs(roles) do 
				if roleName == role1 then 
					roleID1 = roleID;
				end
			end
		end
	end
	if checkStr2 then
		local roles2 = Config.RoleList;
		for roleRef, roleID in pairs(roles2) do 
			if roleRef == role2 then 
				roleID2 = roleID;
				searchGuild2 = false;
			end
		end 
		if searchGuild2 then 
			local roles = GetGuildRoleList(guild);
			for roleName, roleID in pairs(roles) do 
				if roleName == role2 then 
					roleID2 = roleID;
				end
			end
		end
	end
	if tonumber(roleID1) == tonumber(roleID2) then 
		return true;
	end
	return false;
end

function IsDiscordEmailVerified(user) 
    local discordId = nil
    local isVerified = false;
    for _, id in ipairs(GetPlayerIdentifiers(user)) do
        if string.match(id, "discord:") then
            discordId = string.gsub(id, "discord:", "")
            break
        end
    end
    if discordId then 
        local endpoint = ("users/%s"):format(discordId)
        local member = DiscordRequest("GET", endpoint, {})
        if member.code == 200 then
            local data = json.decode(member.data)
            if data ~= nil then 
                -- It is valid data 
                --print("The data for User " .. GetPlayerName(user) .. " is: ");
                --print(data.avatar);
                isVerified = data.verified;
                --print("---")
            end
        else 
        	print("[Badger_Perms] ERROR: Code 200 was not reached. DETAILS: " .. error_codes_defined[member.code]);
        end
    end
    return isVerified;
end

function GetDiscordEmail(user) 
    local discordId = nil
    local emailData = nil;
    for _, id in ipairs(GetPlayerIdentifiers(user)) do
        if string.match(id, "discord:") then
            discordId = string.gsub(id, "discord:", "")
            break
        end
    end
    if discordId then 
        local endpoint = ("users/%s"):format(discordId)
        local member = DiscordRequest("GET", endpoint, {})
        if member.code == 200 then
            local data = json.decode(member.data)
            if data ~= nil then 
                -- It is valid data 
                --print("The data for User " .. GetPlayerName(user) .. " is: ");
                --print(data.avatar);
                emailData = data.email;
                --print("---")
            end
        else 
        	print("[Badger_Perms] ERROR: Code 200 was not reached. DETAILS: " .. error_codes_defined[member.code])
        end
    end
    return emailData;
end

function GetDiscordName(user) 
    local discordId = nil
    local nameData = nil;
    for _, id in ipairs(GetPlayerIdentifiers(user)) do
        if string.match(id, "discord:") then
            discordId = string.gsub(id, "discord:", "")
            break
        end
    end
    if discordId then 
        local endpoint = ("users/%s"):format(discordId)
        local member = DiscordRequest("GET", endpoint, {})
        if member.code == 200 then
            local data = json.decode(member.data)
            if data ~= nil then 
                -- It is valid data 
                --print("The data for User " .. GetPlayerName(user) .. " is: ");
                --print(data.avatar);
                nameData = data.username .. "#" .. data.discriminator;
                --print("---")
            end
        else 
        	print("[Badger_Perms] ERROR: Code 200 was not reached. DETAILS: " .. error_codes_defined[member.code])
        end
    end
    return nameData;
end

function GetGuildIcon(guild --[[optional]])
  local guildId = GetGuildId(guild)
	local guild = DiscordRequest("GET", "guilds/"..guildId, {})
	if guild.code == 200 then
		local data = json.decode(guild.data)
		if (data.icon:sub(1, 1) and data.icon:sub(2, 2) == "_") then 
			-- It's a gif 
			return 'https://cdn.discordapp.com/icons/' .. Config.Guild_ID .. "/" .. data.icon .. ".gif";
		else 
			-- Image 
			return 'https://cdn.discordapp.com/icons/' .. Config.Guild_ID .. "/" .. data.icon .. ".png";
		end 
	else
		print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(guild.data or guild.code)) 
	end
	return nil;
end

function GetGuildSplash(guild --[[optional]])
  local guildId = GetGuildId(guild)
	local guild = DiscordRequest("GET", "guilds/"..guildId, {})
	if guild.code == 200 then
		local data = json.decode(guild.data)
		-- Image 
		return 'https://cdn.discordapp.com/splashes/' .. Config.Guild_ID .. "/" .. data.icon .. ".png";
	else
		print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(guild.data or guild.code)) 
	end
	return nil;
end 

function GetGuildName(guild --[[optional]])
  local guildId = GetGuildId(guild)
	local guild = DiscordRequest("GET", "guilds/"..guildId, {})
	if guild.code == 200 then
		local data = json.decode(guild.data)
		-- Image 
		return data.name;
	else
		print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(guild.data or guild.code)) 
	end
	return nil;
end

function GetGuildDescription(guild --[[optional]])
  local guildId = GetGuildId(guild)
	local guild = DiscordRequest("GET", "guilds/"..guildId, {})
	if guild.code == 200 then
		local data = json.decode(guild.data)
		-- Image 
		return data.description;
	else
		print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(guild.data or guild.code)) 
	end
	return nil;
end

function GetGuildMemberCount(guild --[[optional]])
  local guildId = GetGuildId(guild)
	local guild = DiscordRequest("GET", "guilds/"..guildId.."?with_counts=true", {})
	if guild.code == 200 then
		local data = json.decode(guild.data)
		-- Image 
		return data.approximate_member_count;
	else
		print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(guild.data or guild.code)) 
	end
	return nil;
end

function GetGuildOnlineMemberCount(guild --[[optional]])
  local guildId = GetGuildId(guild)
	local guild = DiscordRequest("GET", "guilds/"..guildId.."?with_counts=true", {})
	if guild.code == 200 then
		local data = json.decode(guild.data)
		return data.approximate_presence_count;
	else
		print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(guild.data or guild.code)) 
	end
	return nil;
end

function GetDiscordAvatar(user) 
    local discordId = nil
    local imgURL = nil;
    for _, id in ipairs(GetPlayerIdentifiers(user)) do
        if string.match(id, "discord:") then
            discordId = string.gsub(id, "discord:", "")
            break
        end
	end
	if discordId then 
		if Caches.Avatars[discordId] == nil then 
			local endpoint = ("users/%s"):format(discordId)
			local member = DiscordRequest("GET", endpoint, {})
			if member.code == 200 then
				local data = json.decode(member.data)
				if data ~= nil and data.avatar ~= nil then 
					-- It is valid data 
					--print("The data for User " .. GetPlayerName(user) .. " is: ");
					--print(data.avatar);
					if (data.avatar:sub(1, 1) and data.avatar:sub(2, 2) == "_") then 
						--print("IMG URL: " .. "https://cdn.discordapp.com/avatars/" .. discordId .. "/" .. data.avatar .. ".gif")
						imgURL = "https://cdn.discordapp.com/avatars/" .. discordId .. "/" .. data.avatar .. ".gif";
					else 
						--print("IMG URL: " .. "https://cdn.discordapp.com/avatars/" .. discordId .. "/" .. data.avatar .. ".png")
						imgURL = "https://cdn.discordapp.com/avatars/" .. discordId .. "/" .. data.avatar .. ".png"
					end
					--print("---")
				end
			else 
				print("[Badger_Perms] ERROR: Code 200 was not reached. DETAILS: " .. error_codes_defined[member.code])
			end
			Caches.Avatars[discordId] = imgURL;
		else 
			imgURL = Caches.Avatars[discordId];
		end 
	else 
		print("[Badger_Perms] ERROR: Discord ID was not found...")
	end
    return imgURL;
end

Caches = {
	Avatars = {},
  RoleList = {}
}
function ResetCaches()
	Caches = {
    Avatars = {},
    RoleList = {},
  };
end

function GetGuildRoleList(guild --[[optional]])
  local guildId = GetGuildId(guild)
	if (Caches.RoleList[guildId] == nil) then 
		local guild = DiscordRequest("GET", "guilds/"..guildId, {})
		if guild.code == 200 then
			local data = json.decode(guild.data)
			-- Image 
			local roles = data.roles;
			local roleList = {};
			for i = 1, #roles do 
				roleList[roles[i].name] = roles[i].id;
			end
			Caches.RoleList[guildId] = roleList;
		else
			print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(guild.data or guild.code)) 
			Caches.RoleList = nil;
		end
	end
	return Caches.RoleList[guildId];
end

local recent_role_cache = {}

function GetDiscordRoles(user, guild --[[optional]])
	local discordId = nil
  local guildId = GetGuildId(guild)
	for _, id in ipairs(GetPlayerIdentifiers(user)) do
		if string.match(id, "discord:") then
			discordId = string.gsub(id, "discord:", "")
			break;
		end
	end

	if discordId then
		if Config.CacheDiscordRoles and recent_role_cache[discordId] and recent_role_cache[discordId][guildId] then
			return recent_role_cache[discordId][guildId]
		end
		local endpoint = ("guilds/%s/members/%s"):format(guildId, discordId)
		local member = DiscordRequest("GET", endpoint, {})
		if member.code == 200 then
			local data = json.decode(member.data)
			local roles = data.roles
			local found = true
			if Config.CacheDiscordRoles then
        recent_role_cache[discordId] = recent_role_cache[discordId] or {}
				recent_role_cache[discordId][guildId] = roles
				Citizen.SetTimeout(((Config.CacheDiscordRolesTime or 60)*1000), function() recent_role_cache[discordId][guildId] = nil end)
			end
			return roles
		else
			print("[Badger_Perms] ERROR: Code 200 was not reached... Returning false. [Member Data NOT FOUND] DETAILS: " .. error_codes_defined[member.code])
			return false
		end
	else
		print("[Badger_Perms] ERROR: Discord was not connected to user's Fivem account...")
		return false
	end
	return false
end

function GetDiscordNickname(user, guild --[[optional]])
	local discordId = nil
  local guildId = GetGuildId(guild)
	for _, id in ipairs(GetPlayerIdentifiers(user)) do
		if string.match(id, "discord:") then
			discordId = string.gsub(id, "discord:", "")
			break
		end
	end

	if discordId then
		local endpoint = ("guilds/%s/members/%s"):format(guildId, discordId)
		local member = DiscordRequest("GET", endpoint, {})
		if member.code == 200 then
			local data = json.decode(member.data)
			local nickname = data.nick
			return nickname;
		else
			print("[Badger_Perms] ERROR: Code 200 was not reached. Error Code: " .. error_codes_defined[member.code])
			return nil;
		end
	else
		print("[Badger_Perms] ERROR: Discord was not connected to user's Fivem account...")
		return nil;
	end
	return nil;
end

Citizen.CreateThread(function()
  local mguild = DiscordRequest("GET", "guilds/"..Config.Guild_ID, {})
  if mguild.code == 200 then
    local data = json.decode(mguild.data)
    print("[Badger_Perms] Successful connection to Guild : "..data.name.." ("..data.id..")")
  else
    print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(mguild.data and json.decode(mguild.data) or mguild.code)) 
  end
  if (Config.Multiguild) then 
    for _,guildID in pairs(Config.Guilds) do
      local guild = DiscordRequest("GET", "guilds/"..guildID, {})
      if guild.code == 200 then
        local data = json.decode(guild.data)
        print("[Badger_Perms] Successful connection to Guild : "..data.name.." ("..data.id..")")
      else
        print("[Badger_Perms] An error occured, please check your config and ensure everything is correct. Error: "..(guild.data and json.decode(guild.data) or guild.code)) 
      end
    end
  end
end)

Thanks for your time.

Please insert the script i provided into a new ressource folder.
With a fxmanifest loading both files

Ok, new resource created with the client and server.lua files and an fxmanifest resource file. Do I need to amend any of my spawnmanager.lua file or my map.lua file?

You need to change the groups so it matches badger config, with your discord roles, and the locations of the TP :smiley:
Then it should work

Thanks again… Here is what I have setup for badger discord and spawn points.

badger discord config.lua

Config = {
	Guild_ID = 'XXXXXXXXXX',   
  	Bot_Token = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
	RoleList = {
            ["Fire Service"] = 1001998682364854342,
		    ["Police Recruit"] = 999507099589558313,
            ["LAPD Vespucci Station"] = 999362677287694476,
            ["LAPD Vinewood Station"] = 999363019677118586,
            ["LAPD La Mesa"] = 999363109930147891,
            ["CHP Headquarters"] = 1001958706033995897,
            ["LASD Headquarters"] = 1001952790203080705,
		    ["LAPD Headquarters"] = 999550580907331636
	},
	CacheDiscordRoles = true, -- true to cache player roles, false to make a new Discord Request every time
	CacheDiscordRolesTime = 60, -- if CacheDiscordRoles is true, how long to cache roles before clearing (in seconds)
}

This is my discordaceperms roles- I am hoping I have set up the groups correctly, however, would I need to set the ace for each of these groups?

Config = {
	Server_Name = "xxxxxxx",
	Discord_Link = 'xxxxxxxxxxx',
	Website_Link = 'xxxxxxxxxx',
	roleList = {
		{1001998682364854342, "group.FireService"},
		{999507099589558313, "group.PoliceRecruit"},
		{999362677287694476, "group.LAPDVespucci Station"},
		{999363019677118586, "group.LAPDVinewood Station"},
		{999363109930147891, "group.LAPDLa Mesa"},
                {1001958706033995897, "group.CHPHeadquarters"},
                {1001952790203080705, "group.LASDHeadquarters"},
		{999550580907331636, "group.LAPDHeadquarters"},	                       
	},
}

And here is my map.lua file -

spawnpoint 's_m_y_cop_01' { x = 434.02, y = -995.27, z = 30.69 } --mrpd
spawnpoint 's_m_y_cop_01' { x = 446.32, y = -985.7, z = 34.97 } --lapd
spawnpoint 's_m_y_cop_01' { x = 359.15, y = -1590.52, z = 34.25 } --lasd
spawnpoint 's_m_y_cop_01' { x = -3163.99, y = 1111.74, z = 21.07 } --chp
spawnpoint 's_m_y_cop_01' { x = 422.58, y = -1095.8, z = 30.06 } --policeacademy
spawnpoint 's_m_y_cop_01' { x = 855.90, y = -1285.41, z = 28.24 } --lamesa
spawnpoint 's_m_y_cop_01' { x = 638.26, y = 1.79, z = 82.79 } --vinewood
spawnpoint 's_m_y_cop_01' { x = -1067.54, y = -819.00, z = 23.46 } --vespucci
spawnpoint 's_m_y_cop_01' { x = 213.19, y = -1658.56, z = 29.80 } --lafd

Has this been figured out? I followed this topic and yet my players are still spawning in random locations.

1 Like

+1 I also need to know