Creating Entities on the Client
Dalin Seivewright
0x0000221E Join Date: 2007-10-20 Member: 62685Members, Constellation
in Modding
<div class="IPBDescription">Programmers Welcome</div>Has anyone figured out how to create and render entities on the client? As far as I understand, an Entity isn't really an entity until the OnCreate method is called and that is only ever called when Server.CreateEntity is executed. So, all Update events (time, rendering, etc.) don't call the appropriate events on an object until it has properly been created in the game world. I need to create entities on the client without a server existing. Any ideas?
Comments
However, you can make clientside stuff which has a model I think, if thats all you want to make.
<a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/RenderModel" target="_blank">http://www.unknownworlds.com/ns2/wiki/inde...cts/RenderModel</a>
Not really the same as creating a "entity" (the class named entity) clientside though, as rendermodel is purely graphics...
To answer Dalin's question though, I don't think it can be done, at least not without seriously cannibalizing NS2's Lua-scripts. This because there is a clear Master-Slave relation between the Server and the Client, and clients aren't allowed to just go about creating their own little world without the server knowing about it.
You could argue that there are exceptions for things that aren't relevant for other clients (or the server) to know about. The most poinent example are the client-side (beside the server-side) physics in HL2, which simply aren't propagated to other clients. I'm uncertain whether comparable things are already present in NS2, but even if there are, it may be so that they must initially be created by the server before being handed over to the clients, in which case you're no further than you are now.
<a href="http://www.unknownworlds.com/ns2/wiki/index.php/Lua/Objects/RenderModel" target="_blank">http://www.unknownworlds.com/ns2/wiki/inde...cts/RenderModel</a><!--QuoteEnd--></div><!--QuoteEEnd-->
Yeah, I don't need an actual Entity, I just need something that I can render on to the screen with. A RenderModel would do just that however, as far as I can tell, the engine won't render it unless it is told it can (which seems to only be when there is a game world state created, i.e. when a server is running). I've been trying to test object rendering on the client with a localhost server running in the background but I can't quite get a custom bare-minimum camera/player working.
<!--quoteo--><div class='quotetop'>QUOTE </div><div class='quotemain'><!--quotec-->@Dalin my understanding is that a server always exists... either you're joining someone else's or you hosting one locally. Either way the 'server' lua is always being run somewhere. Someone correct me if I'm wrong.<!--QuoteEnd--></div><!--QuoteEEnd-->
A server, at least logically, will always be running except (now I may be wrong here) when the game initially starts. At this point all your code is being run from Client.lua and the Server context doesn't exist (try it, Server will be nil... which makes sense, really, but still... Main is also nil so Client.lua is executed before a lot of the powerhouse libraries are loaded it seems). The Cinematic system works in an odd way where it doesn't seem to need to create a server and the client handles the object creation without dealing with actual entities.. but more investigation is going to be required. I did just notice some interesting code in ns2/Client.lua that I missed last night, which may be the ticket.
<!--QuoteBegin-"player"+--><div class='quotetop'>QUOTE ("player")</div><div class='quotemain'><!--QuoteEBegin-->I think he ment doing it without the help of the Server-VM, as you are indeed right pointing out there isn't a Client-VM without a Server-VM (obvious exception here being the main-menu).<!--QuoteEnd--></div><!--QuoteEEnd-->
Pretty much this. I just want to draw stuff on the screen like I normally would without having to worry about the server state or what the server wants me to do. I guess if futher investagations don't give me anything, a 'Window' Server will have to be created which will control all menu components, which just seems stupid considering that same server would be running the game... or I'll have to wait for their custom GUI implemenation to be done.
[
*edit* I was partially wrong about this. The prop and light tables that I thought were in the Client library are actually declared in ns2/Client.lua so somewhere a render call is made using those tables, which is what we're after.
<!--quoteo(post=1815536:date=Dec 13 2010, 09:15 AM:name=Delphic)--><div class='quotetop'>QUOTE (Delphic @ Dec 13 2010, 09:15 AM) <a href="index.php?act=findpost&pid=1815536"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Ahh, I apologise for insulting your intelligence! Let us know if that Lua you found renders something useful. I'm currently attempting to render the main menu using their GUI system rather than flash (it's not going so well though, hopefully they'll be doing it internally soon).<!--QuoteEnd--></div><!--QuoteEEnd-->
I saw no insult, I just wanted to add in something else I noticed. And the server context IMO should still be visible to the Client, even though it might not make sense :) I finally got around to doing more digging. It turns out that the Client does actually handle most of the map stuff as far as rendering goes. The client keeps a list of static props and lights. For static props, it creates a RenderModel and adds it to a table in the Client library. For lights, it uses a special function in the Client library (Client.CreateRenderLight) which returns a special light entity[1] which is added to a light entity table, also in the Client library.
Given that, it seems the client only has a limited amount of control over the whole process. I'm not quite sure how restrictive the static prop table is in Client, but we might be able to add our custom GUI objects to it then treat the objects in that table a dynamic either by keeping a table yourself of GUI objects with pointers to indexes in the Client table, or by simply keeping a table of indexes and running through each item directly.
One caveat might exist though. So far, I've only gotten this working with the server running but that is only because I haven't played around with the static props yet. To render anything, you need to hook into the SetupCamera event (code below, in case you were wondering). A method hooking into it should accept a Camera as an argument which means the Engine must create a camera on start up. The SetupCamera event is fired every frame, regardless of whether or not you're connected to a server. If you're not connected to a server, you'll only see blackness but if you start a server you'll end up seeing the light fog that lights up really dark areas of maps (this is how you know its working without getting the lights in the map to create themselves).
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//Place in your custom Client.lua
function OnSetupCamera(camera)
local camangles = Angles(0,0,0) // no rotation
local camcoords = Coords(camangles:GetCoords())
camcoords.origin = Vector(0,0,0) //origin is at 0,0,0
camera:SetCoords( camcoords ) //sets the camera created by the engine to the desired locaton (origin) and rotation
camera:SetFov( math.rad(90) ) //90 is default used by NS2
return true
end
Event.Hook("SetupCamera", OnSetupCamera)<!--c2--></div><!--ec2-->
So this whole entity creation on the Client might be very possible after all. I won't go over specifics on static prop and light creation just yet, but the relevant stuff can actually be found in the OnMapLoadEntity found in ns2/Client.lua. This method is called with every entity found in a map, but the way it handles things can be done in another unrelated method so you won't have to worry about starting a server.
Next I will try and get some static props and lights being rendered without a server. If you want map geometry, you'll probably need to start a server or use Client.CreateCinematic (info can also be found in the OnMapLoadEntity method found in ns2/Client.lua)
[1] Map entities aren't real entities apparently, which explains why we can't add our own enities to the editor yet, AFAIK.
You first need to set up a proper camera:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function OnSetupCamera(camera)
local camangles = Angles(0,0,0) // no rotation
local camcoords = Coords(camangles:GetCoords())
camcoords.origin = Vector(0,0,0) //origin is at 0,0,0
camera:SetCoords( camcoords ) //sets the camera created by the engine to the desired locaton (origin) and rotation
camera:SetFov( math.rad(90) ) //90 is default used by NS2
return true
end
Event.Hook("SetupCamera", OnSetupCamera)<!--c2--></div><!--ec2-->
To create a light source (without which you won't be able to see anything... that ambient fog only exists when a map is loaded so you'll just see pitch black otherwise):
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local renderLight = Client.CreateRenderLight()
local angles = Angles(0,0,0)
local desiredlocation = Vector(0,0,0)
local coords = angles:GetCoords(desiredlocation)
local colors = Color(32,0,32,32)
renderLight:SetCoords( coords )
renderLight:SetRadius( 512 )
renderLight:SetIntensity( 5 )
renderLight:SetColor( colors )<!--c2--></div><!--ec2-->
To create something you can actually see, you'll need a render model. Render models are always rendered, regardless of any connection to a server, but SetIsVisible(false) will turn rendering for a given render model off.
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local renderModel = Client.CreateRenderModel(RenderScene.Zone_Default)
renderModel:SetModel("models/male/male.model") //note: this can also be used with a precached model index
local angles = Angles(0,0,0)
local desiredlocation = Vector(0,0,0)
local coords = angles:GetCoords(desiredlocation)
renderModel:SetCoords(coords)
renderModel:SetIsStatic(true)
renderModel:SetIsInstanced(true)
renderModel:SetIsVisible(true)<!--c2--></div><!--ec2-->
The previous two code snippets can be placed -anywhere- in your Client.lua. As long as the code is executed at some point. I believe that all lights and render models exist even after you connect to a server. A table is kept of all static resources (props/lights) so that when a connection is closed, the table can be run through and all props/lights destroyed.
Lights don't work very well outside of geometry though, it seems. It may have something to do with the RenderScene that is used when creating the render model. It is also good to note that I'm not entirely sure how their Coordinate class works. The x, y and z axis variables don't seem to be used for actual model placement, just for rotation and scaling. The origin seems to determine where the object is placed however, you need to create a Coordinate object from an Angle rather than just creating a Coordinate object and setting its origin.
PS: An angle of (0,0,0) sets the camera looking towards positive Z, not negative Z (if you're familiar with OpenGL).