Einlander Posted August 22, 2017 Share Posted August 22, 2017 I've spent the last few days creating a simple self contained server browser. It will list all the servers of a specific game, allow the user to select a server and send a callback with the selected info. Requirements: The script from this topic placed in a file here: Scripts/Functions/eventqueuemanager.lua The Script: --[[ Name: serverBrowser Author: Einlander Date Completed: 2017-8-22 Description: A Server Browser for Leadwerks Notes: This is a rather simple Server Browser script that allows for listing of servers and selection. -2017-8-24: -Added the ability to directly connect to a ip and port. -Moved the code to put the window into a function and check on server browser creation. -2017-8-25: -Added the abilitiy to automatically resize to fit inside the bounds of the current context --]] --TODO: Add manual server connect import("Scripts/Functions/eventqueuemanager.lua") function serverBrowser(gameName,context,x,y,width,height) local self = {} self.gameName="" local _context local serverBrowser = {} local ServerBrowserClient = Client:Create() local callback = {} local movemode = false local currentWindow --[[ Description: Initializes and Draws the GUI Parameters: x as integer y as integer width as integer height as integer Notes: None ]] local function createUI(x,y,width,height) x = ((tonumber(x)~= nil) and x or 100) y = ((tonumber(y)~= nil) and y or 100) width = ((tonumber(width)~= nil) and width or 500) height = ((tonumber(height)~= nil) and height or 300) serverBrowser.gui = GUI:Create(_context) serverBrowser.gui:Hide() serverBrowser.root = serverBrowser.gui:GetBase() serverBrowser.root:SetLayout(x,y,width,height) serverBrowser.panel = Widget:Panel(0,0,width,height,serverBrowser.root) serverBrowser.closeButton = Widget:Button("X",1,1,19,19,serverBrowser.panel ) serverBrowser.closeButton:SetLayout(serverBrowser.panel:GetSize().x-serverBrowser.closeButton:GetSize().x - 10,1,19,19) -- hides browser when clicked, regardless of results serverBrowser.moveButton = Widget:Button("+",10,1,19,19,serverBrowser.panel ) serverBrowser.titlelabel = Widget:Label("Server Browser",40,5,100,25,serverBrowser.panel) serverBrowser.serveriplabel = Widget:Label("IP/Address:",15,serverBrowser.panel:GetSize().y - 23,100,25,serverBrowser.panel) serverBrowser.serverportlabel = Widget:Label("Port:",290,serverBrowser.panel:GetSize().y - 23,100,25,serverBrowser.panel) serverBrowser.serveripfield = Widget:TextField("",85,serverBrowser.panel:GetSize().y - 30, 200, 25, serverBrowser.panel) serverBrowser.serverportfield = Widget:TextField("",320,serverBrowser.panel:GetSize().y - 30,80,25,serverBrowser.panel) serverBrowser.connectButton = Widget:Button("Connect",405,serverBrowser.panel:GetSize().y - 30,55,25,serverBrowser.panel ) serverBrowser.serverList = Widget:ListBox(10,20,serverBrowser.panel:GetSize().x - 20,serverBrowser.panel:GetSize().y - 100,serverBrowser.panel) serverBrowser.refreshButton = Widget:Button("Refresh",10,serverBrowser.panel:GetSize().y - 70,55,25,serverBrowser.panel ) serverBrowser.joinButton = Widget:Button("Join",0,0,55,25,serverBrowser.panel ) serverBrowser.joinButton:SetLayout(serverBrowser.panel:GetSize().x-serverBrowser.refreshButton:GetSize().x - 10,serverBrowser.panel:GetSize().y - 70,55,25) serverBrowser.gui:Show() end --[[ Description: Resizes the GUI Parameters: Width as integer Height as integer Notes: none ]] function self:SetSize(width,height) serverBrowser.root:SetLayout(serverBrowser.root:GetPosition().x,serverBrowser.root:GetPosition().y,width,height) serverBrowser.panel:SetLayout(0,0,width,height) serverBrowser.closeButton:SetLayout(serverBrowser.panel:GetSize().x-serverBrowser.closeButton:GetSize().x - 10,1,19,19) -- hides browser when clicked, regardless of results serverBrowser.moveButton:SetLayout(10,1,19,19) serverBrowser.titlelabel:SetLayout(40,5,100,25) serverBrowser.serveriplabel:SetLayout(15,serverBrowser.panel:GetSize().y - 23,100,25) serverBrowser.serverportlabel:SetLayout(290,serverBrowser.panel:GetSize().y - 23,100,25) serverBrowser.serveripfield:SetLayout(85,serverBrowser.panel:GetSize().y - 30, 200, 25) serverBrowser.serverportfield:SetLayout(320,serverBrowser.panel:GetSize().y - 30,80,25) serverBrowser.connectButton:SetLayout(405,serverBrowser.panel:GetSize().y - 30,55,25) serverBrowser.serverList:SetLayout(10,20,serverBrowser.panel:GetSize().x - 20,serverBrowser.panel:GetSize().y - 100) serverBrowser.refreshButton:SetLayout(10,serverBrowser.panel:GetSize().y - 70,55,25) serverBrowser.joinButton:SetLayout(serverBrowser.panel:GetSize().x-serverBrowser.refreshButton:GetSize().x - 10,serverBrowser.panel:GetSize().y - 70,55,25) end --[[ Description: Returns the server browsers size Parameters: ScaledCoords as boolean Notes: scaledCoords: if set to false logical coordinates will be returned, otherwise scaled coordinates will be returned. ]] function self:GetSize(ScaledCoords) return serverBrowser.root:GetSize(ScaledCoords == true and true or false ) end --[[ Description: Shows UI Parameters: none Notes: none ]] function self:Show() serverBrowser.gui:Show() serverBrowser.root:Enable() self.context:GetWindow():ShowMouse() end --[[ Description: Hides UI Parameters: none Notes: none ]] function self:Hide() serverBrowser.gui:Hide() serverBrowser.root:Disable() end --[[ Description: Returns if UI is hidden or not Parameters: none Notes: none ]] function self:Hidden() return serverBrowser.gui:Hidden() end --[[ Description: Sets a external function to call when server is selected or window is closed. Parameters: callback as function Notes: --self:SetCallback is an overloaded function --self:SetCallback(callback) simple callback, used on functions not attached to an entity --self:SetCallback(self, callback) Used when adding from a script attached to an entity (calling from and to any function that starts with Script: ) self:SetCallback requires the callback function to have these 3 parameters: (ip,port,close) Ip is the server address as a string port is unused as of now since the leadwerks master server does not return it and also the remoteserver class has no property for it close is a boolean that retuns true if the serverbrowser is closed if it is closed it will return (nil,nil,true) if a server is chosen and connect is clicked, it will return ("address",nil,true) it will also close the window. Take care of how the callback handles the data (recursion is possible if not thought out properly) ]] function self:SetCallback(_callback) --if (#args==1) then if type(_callback) ~= "function" then return false end callback.functionValue = _callback --elseif (#args==2) then --if type(args[2]) ~= "function" then return false end --callback.selfValue = args[1] --callback.functionValue = args[2] --else --return false --end end --[[ Description: Removes the callback Parameters: none Notes: none ]] function self:ClearCallback() --callback.selfValue = nil callback.functionValue = nil end --[[ Description: Moves the serverbrowser back into the window Parameters: non Notes: none ]] local function FitInScreen() -- resize to fit inside current context if serverBrowser.root:GetSize().x > _context:GetWidth() then self:SetSize(_context:GetWidth(), serverBrowser.root:GetSize().y) end if serverBrowser.root:GetSize().y > _context:GetHeight() then self:SetSize(serverBrowser.root:GetSize().x, _context:GetHeight() ) end -- move it into view if serverBrowser.root:GetPosition().x + serverBrowser.root:GetSize().x > _context:GetWidth() then serverBrowser.root:SetLayout(serverBrowser.root:GetPosition().x - ( (serverBrowser.root:GetPosition().x + serverBrowser.root:GetSize().x) - _context:GetWidth()) ,serverBrowser.root:GetPosition().y ,serverBrowser.root:GetSize().x,serverBrowser.root:GetSize().y) end if serverBrowser.root:GetPosition().x < 0 then serverBrowser.root:SetLayout(0,serverBrowser.root:GetPosition().y ,serverBrowser.root:GetSize().x,serverBrowser.root:GetSize().y) end if serverBrowser.root:GetPosition().y + serverBrowser.root:GetSize().y > _context:GetHeight() then serverBrowser.root:SetLayout(serverBrowser.root:GetPosition().x ,serverBrowser.root:GetPosition().y - ( (serverBrowser.root:GetPosition().y + serverBrowser.root:GetSize().y) - _context:GetHeight()),serverBrowser.root:GetSize().x,serverBrowser.root:GetSize().y) end if serverBrowser.root:GetPosition().y < 0 then serverBrowser.root:SetLayout(serverBrowser.root:GetPosition().x, 0 ,serverBrowser.root:GetSize().x,serverBrowser.root:GetSize().y) end end --[[ Description: Processes all the events recived Parameters: event as Event Notes: none ]] local function ProcessEvent(event) if event.id == Event.WidgetSelect then elseif event.id == Event.WidgetAction then if movemode == true and (event.source == serverBrowser.gui) then _context:SetColor(0,0,0,0) -- need to clear alpha!!! otherwise you wont see the 3d serverBrowser.gui:Clear() serverBrowser.root:SetLayout(currentWindow:GetMousePosition().x - serverBrowser.moveButton:GetSize().x , currentWindow:GetMousePosition().y - (serverBrowser.moveButton:GetSize().y/2) ,serverBrowser.root:GetSize().x,serverBrowser.root:GetSize().y) EventQueue:Emit(Event.WidgetAction,serverBrowser.gui) if currentWindow:MouseHit(Key.LButton) then -- make sure it doesnt escape the screen movemode = false FitInScreen() _context:SetColor(0,0,0,0) serverBrowser.gui:Clear() serverBrowser.root:Enable() end serverBrowser.panel:Redraw() return true end if event.source == serverBrowser.moveButton then movemode = true currentWindow = Window:GetCurrent() EventQueue:Emit(Event.WidgetAction,serverBrowser.gui) _context:SetColor(0,0,0,0) serverBrowser.gui:Clear() serverBrowser.root:Disable() currentWindow:FlushMouse() return true end if event.source == serverBrowser.closeButton then serverBrowser.root:Disable() serverBrowser.gui:Hide() --System:Print(self.gameName .. ":: Close button") if callback.functionValue ~= nil then callback.functionValue(nil,nil,true) end self:Release() return true end if event.source == serverBrowser.connectButton then local serverip = ((string.len(serverBrowser.serveripfield:GetText())>0) and serverBrowser.serveripfield:GetText() or nil) local serverport = ((tonumber(serverBrowser.serverportfield:GetText()) ~= nil) and tonumber(serverBrowser.serverportfield:GetText()) or nil) if callback.functionValue ~= nil then local cb = callback.functionValue -- release everything to minimize mem use in case of consecutive server hopping.abs self:Release() cb(serverip,serverport,true)-- should it close when they chose a server? return true end return false end if event.source == serverBrowser.joinButton then --System:Print(self.gameName .. ":: Connect button") if serverBrowser.serverList:GetSelectedItem() > -1 then local servercount = ServerBrowserClient:CountServers(self.gameName) -- has a super high chance of screwing up when clicked, i mean REALLY HIGH local remoteServerData = ServerBrowserClient:GetServer(serverBrowser.serverList:GetSelectedItem()) --System:Print("Server Name::" .. remoteServerData.address .."#### Description" .. remoteServerData.description) serverBrowser.root:Disable() serverBrowser.gui:Hide() if callback.functionValue ~= nil then local cb = callback.functionValue local addy = remoteServerData.address -- there is no port :/ remoteServerData:Release() -- release everything to minimize mem use in case of consecutive server hopping. self:Release() cb(addy,nil,true)-- should it close when they chose a server? return true end end return false end if event.source == serverBrowser.refreshButton then --System:Print(self.gameName .. ":: Refresh button") self:PopulateServers(self.gameName) return true end end return true end --[[ Description: Hides UI Parameters: none Notes: none ]] function self:Update(event) if serverBrowser == nil then return false end if serverBrowser.gui:Hidden() then return false end if event:GetClassName() == "Event" then ProcessEvent(event) end return true end --[[ Description: Adds Servers to the server list Parameters: gameName as string Notes: Gamename is the exact name of the game the server registered ]] function self:PopulateServers(gameName) if gameName == nil then return end local servercount = ServerBrowserClient:CountServers(gameName) --System:Print("Server Count::" .. tostring(servercount)) serverBrowser.serverList:ClearItems() serverBrowser.serverList:SelectItem(-1) for i=0, servercount-1 do remoteServerData = ServerBrowserClient:GetServer(i) -- System:Print("Server Name::" .. remoteServerData.address .."#### Description" .. remoteServerData.description) serverBrowser.serverList:AddItem("["..remoteServerData.address.."] ["..remoteServerData.description.."]") end end --[[ Description: Initializes Class Parameters: gameName as string context as Context x as integer y as y as integer width as integer height as integer Notes: Gamename is the exact name of the game the server registered ]] local function init(gameName,context,x,y,width,height) -- check for the context _context = context createUI(x,y,width,height) FitInScreen() self.gameName = gameName self:PopulateServers(self.gameName) EventQueueManager:AddListener(self.Update) end --[[ Description: Unloads the class and all it's componentes Parameters: none Notes: none ]] function self:Release() for item in pairs(self) do if (type(self[item]) == "function") or (type(self[item]) == "userdata") then self[item] = nil end end self.gameName = nil -- The order is important! serverBrowser.root:Disable() serverBrowser.gui:Hide() serverBrowser.closeButton:Release() serverBrowser.titlelabel:Release() serverBrowser.serverList:Release() serverBrowser.joinButton:Release() serverBrowser.refreshButton:Release() serverBrowser.connectButton:Release() serverBrowser.moveButton:Release() serverBrowser.serveriplabel:Release() serverBrowser.serverportlabel:Release() serverBrowser.serveripfield:Release() serverBrowser.serverportfield:Release() serverBrowser.panel:Release() serverBrowser.root:Release() serverBrowser.gui = nil serverBrowser=nil ServerBrowserClient:Release() _context = nil callback = nil movemode = nil self.Show = nil self = nil end init(gameName,context,x,y,width,height) return self end A simple example: It will create a server and open a server browser window. + Starts the move mode, click to exit it. X closes the window. import("Scripts/Functions/eventqueuemanager.lua") import("Scripts/serverbrowser.lua") local window = Window:Create() local context = Context:Create(window) local server = Server:Create() System:Print("Server1 publish::" .. tostring(server:Publish("Example Temp Test Server", "Testing population of server list from masterserver "))) server:Release() function test(ip,port,close) -- callback function System:Print("Callback") System:Print(ip) System:Print(tostring((close == true) and "close" or "")) end local serverbrowser = serverBrowser("Example Temp Test Server",context,0,0) -- create the serverbrowser serverbrowser:SetCallback(test) -- set callback -- do stuff while true do if window:Closed() then return end if window:KeyHit(Key.Escape) then return end Time:Update() context:SetColor(0,0,0,0) -- need to clear alpha!! or the gui will stay on screen!! context:Clear() EventQueueManager:Update() context:Sync() end serverbrowser:ClearCallback() -- remove callback serverbrowser:Release() -- release everything The server browser will obey the limits of your window. If any part of it is not on screen, it will be pushed back onto it. Edits: -2017-8-24: -Added the ability to directly connect to a ip and port. -Moved the code to put the window into a function and check on server browser creation. -2017-8-25: -Added the abilitiy to automatically resize to fit inside the bounds of the current context Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.