![]() |
Best way to handle portals? - Printable Version +- Cuberite Forum (https://forum.cuberite.org) +-- Forum: Plugins (https://forum.cuberite.org/forum-1.html) +--- Forum: Plugin Discussion (https://forum.cuberite.org/forum-8.html) +--- Thread: Best way to handle portals? (/thread-2229.html) |
Best way to handle portals? - Zee1234 - 12-01-2015 So I'm currently writing a portals plugin. Right now, it's only going to be for BungeeCord, but eventually I might make it for other things (like multiworld, in world, etc). I've already figure out how I can define areas (either using a worldedit API call or using a special stick to get the two corners), so that's not the problem. The thing I want input on is how best to handle checking if a player is in a portal. There's a number of methods, but it would have to use the onPlayerMoved(ing? one of those) hook, correct? As such, I want to do as minimal a processing as possible. I've come up with a few simple ideas, but each one has downsides. There are also a few other options I've come up with (such as portals being made of specific blocks so I can check for blocktype or air/portal block beneath the player and return false if they don't match), but I'd rather not use those methods. So, here are my ideas: Method 1: Nested for's every ... no. Method 2: Store all portals in, say, g_Portals, in the format g_Portals = { name1: { xmin = 0 xman = 1 ymin = 10 ymax = 100 zmin = 10 zmax = 100 information = { -- information goes here } }, .... } function checkPortal(x,y,z) for k,v in pairs(g_Portals) do if v.xmin <= x and v.xmax >= x and ... and v.zmax <= z then return v.information end end return false end Pro: Handles a small number of any size portals extremely well Con: A large number of portals/players could cause serious server lag. Method 3: On startup, the plugin takes an input table of the form from method 2 (probably actually sqlite information, unless I get lazy again and just use file storage) and produces a table of all coordinates in the form g_PortalBlocks[x_value][y_value][z_value] = "portal_name" and then references another table (say, g_Portals) like g_Portals["portal_name"] = {..information here..} so that the code would like like this function checkPortal(x,y,z) return g_Portals[g_PortalBlocks[x][y][z]] endThen you could just use an if to check if the return is nil, and if not, use the information to teleport. This plugin would use the integer location type for this, so that decimals don't mess everything up. Pros: Incredibly fast, no matter how many people/portals there are. Cons: A large number of portals and/or large portals could cause massive memory usage. Method 4: Using a cCuboid constructed at startup (likely as an element of the g_Portals table at key "portal_name") instead of a custom object in method 2, and therefore using IsInside instead of the chained ands. Pro/Con: Based entirely on how this is different from method 2 in terms of speed/memory usage, and possibly other things. Can't say I know this. Method 5: Use Vector3i for a kind of combination of method 2 and 3. Pro: can't really think of any. Con: Has the downsides of BOTH method 2 and 3 so... probably not. Method 6: ??? I'm currently leaning towards method 3 as I think that the bad situation in method 3 (extremely large (numbers of) portals) is significantly less likely to happen then the bad situation in method 2/4 (large number of players/portals), and can also be somewhat solved by limiting the size of portals. Also because method 3 is a memory issue (that I believe would have to go to extreme measures to cause noticeable impact) whereas method 2 is a "stops the server really frequently" issue. However, I'd love to know any methods that someone else suggests (ex: a good way to use cCuboid or even Vector3(d/f/i) in a way other than listed above). RE: Best way to handle portals? - DiamondToaster - 12-01-2015 When you initialize each portal, instead of storing points, you could use a cBoundingBox. On creating a new portal object, pass two Vector3d objects that define the box's max and min points (MUST be sorted, other words, the min has to contain the lowest coords, the max contains the highest coords). Then use a PlayerOnMoving hook and a for loop to check each portal if the player has walked into it. -- The Portal meta-object Portal = { Volume = nil, Info = "" } -- The Portal constructor function function Portal:new(Min, Max, Info) local o = {} setmetatable(o, Portal) self.__index = self o.Volume = cBoundingBox(Min, Max) o.Info = Info -- Have it insert itself into a global table if desired table.insert(g_Portals, o) -- And return a reference to the new Portal return o end Then, to check if the player is inside the portal defined by the bounding box: function OnPlayerMoving(Player, OldPos, NewPos) -- Iterate through each portal for _, k in pairs(g_Portals) do -- And check if the Players new position is in any portal boxes if k.Volume:IsInside(NewPos) then -- Do something, grab the k.Info stuff and teleport the player to the desired destination attached to this portal -- Also might be a good idea to stop the loop if a match is found to try and reduce overhead return true end end return false end None of this code is tested, but here's a pretty clean way of going about this. RE: Best way to handle portals? - NiLSPACE - 12-01-2015 I'd load all the portals in an X radius around you. When the player reaches the edge you load the next area of portals. That way you don't have to loop through all portals in the world. Just like ProtectionAreas does. |