Jump to content

miko93

Members
  • Posts

    22
  • Joined

  • Last visited

Profile Information

  • Location
    Regensburg, Germany

miko93's Achievements

Newbie

Newbie (1/14)

16

Reputation

  1. Updated the source zip. Removed some unneeded files. It is a tad smaller now
  2. Uhm, I've got the standard here, so can't really check. But as some C compiling is involved (also for the client), I'd assume the full version be needed...
  3. Thought I could write my first Blog entry and report about my ventures into network programming with Leadwerks, ENet and Lua. Also, provide some source code. Okay, here we go: What I wanted to do My test app should be a client-server setup where clients can connect to the server, see each other's avatar, send chat lines and move around their avatar on a plane. Nothing fancy really, just to get into the topic. The ENet network library (http://http://enet.bespin.org/) should be used, and network functions should be exposed from C to Leadwerk's lua engine (as I wanted to do the main 'game coding' in lua). The clients are intended to run on Windows, while the server should first be run as console app on Win, might later be moved to a remote rented Linux server. The Network Concept For the first testing, I tried to make the server part very "light-weight", and leave many things to the client(s). Probably not the right thing to do for a full scale MMOG (mainly due to cheat concerns) - but, well, this test app is far from that anyway ;-) So, the server will - aside from some administrative tasks - mainly receive messages from a client and distribute to the others. As for the communication, a rather simple protocol layer was created on top of ENet (i.e., a definition of message formats and identifiers). Note that not everything a client sends will be distributed - it would first be checked for (some very basic) consistency. Network Messages Seven network messages were defined, some for client->server or server->client only, some for both ways. The messages are wrapped into ENet's 'ENetPacket' and sent, to be evaluated by the receiver. MSG_TYPE_ID_REQ(a clients requests a unique identification number from the server) MSG_TYPE_NAME (a client sends its chat name) MSG_TYPE_POS (a client or the server sends info about position/rotation of an avatar) MSG_TYPE_CHAT (a client or the server sends info about a chat line) MSG_TYPE_CLIENT_ON (the server sends info about a new client online - also used to inform a new client about existing ones) MSG_TYPE_CLIENT_OFF (the server sends info about a client leaving) MSG_TYPE_CLIENT_ID (the server sends a unique identification number to the client that requsted one) The message formats used would look like so (complete source available below): struct MSG_POS // Sending a client pos and rot info { BYTE cMsgType = MSG_TYPE_POS; UINT16 ClientID; float fPosX, fPosY, fPosZ; float fRotX, fRotY, fRotZ; }; The Server This is a win console C app, which should - with some tweaking - compile and run on Linux also. Actually, I started out with doing it on Ubuntu on a VMWare Player, but moved to Win console later on. Just for the ease of use. The server will detect when a new client connected (using ENet functionality), and wait for the initial 'request for ID' message. After assignment, it informs the other clients about the new one (and vice versa). On server side, only the unique ID and the client's chat name are stored. When receiving e.g. a "Chatline" message, the server would check if the sender's client ID and the message type both are valid, then distribute the message to all clients. Upon disconnection of a client (again using ENet functionality), remaining clients would be informed also. The Client Networking here is done as a two-stage concept: Extending the Leadwerks App with some networking C functions and exposing some of them to lua (where the main 'game programming' is to happen). Note that Leadwerks also comes with the ENet headers and libs, so it is not necessary to include it here. You can just use it in your C code. As for the connection between C and lua, two possibilities must be considered: Calling a (new) network function from lua and calling back from C into lua when e.g. a certain message arrived. Exposing a new function to lua would look like this (returning the connected state of the network): lua_register(Interpreter::L, "NW_IsConnected", NW_IsConnected); extern "C" int NW_IsConnected(lua_State* L) { lua_pushboolean(L, gl_Networking.m_bConnected); return 1; } Preparing a callback from C into lua looked like so (this is a callback when a new client is reported by the server). Note that (obviously) the global function "NW_Callback_Connected" must be present in the lua code, to be called. void Callback_Connected(unsigned short int iID) { lua_getglobal(Leadwerks::Interpreter::L, "NW_Callback_Connected") if (!lua_isfunction(Leadwerks::Interpreter::L, -1)) { lua_pop(Leadwerks::Interpreter::L, 1); return; } lua_pushnumber(Leadwerks::Interpreter::L, iID); /* do the call (1 argument, 0 result) */ if (lua_pcall(Leadwerks::Interpreter::L, 1, 0, 0) != 0) { printf("NW_Callback_Connected ERROR: %s\n", lua_tostring(Leadwerks::Interpreter::L, -1)); return; } } For the test app, several functions were exposed to lua from the added network C code, and also several possible callbacks are provided. To avoid any multi-threading issues, the function "NW_Update()" must periodically called from lua (e.g. in WorldUpdate). Within there, callbacks are then triggered. Callbacks, invoked from network (when NW_Update() called) NW_Callback_Connected(id) // called when we are connected to the network NW_Callback_ClientJoined(id) // called when a client joined the network NW_Callback_ClientLeft(id) // called when a client left the network NW_Callback_ClientPosRot(id,Px,Py,Pz,Rx,Ry,Rz) // called when a client pos/rot message arrived NW_Callback_ClientName(id,name) // called when a client sent its name NW_Callback_ClientChat(id,chatline) // called when a client sent a chatline Functions, to be called from lua NW_Connect() // do this once, e.g. in Script:Start() NW_IsConnected() // call this to see if server connection is established NW_Update() // do this regularly, e.g. in Script:UpdateWorld() NW_SendPosRot(Px,Py,Pz,Rx,Ry,Rz) // when pos/rot changes, send all e.g. 200ms NW_SendName(name) // send your client name once (15 chars max); NW_SendChatline(chatline) // send chatlines (255 chars max) to other clients Within lua, it is then the client's task to respond to e.g. a 'NW_Callback_ClientJoined' message. Like, create a new avatar and move it around. The client is sending its own position every 200ms, to be distributed further by the server. Within the client, only very few measures have been taken to compensate for network lag. It will interpolate incoming positions for remote avatars (moving them with constant speed), but does not really apply such high-sophisticated methods like movement prediction and whatnot. To move one's own avatar, use WASD. To submit a chat line, hit enter (submit with enter again, cancel with esc). Note that as for now, a connection attempt is only done at startup. So the server must be started before any client. Also, 'localhost' as server IP is currently hardcoded. Final Words While I did dive into ENet before, this was my rather first time with all that lua mumbo-jumbo. Actually, after getting used to it, writing scripts in lua is quite fast (relaxing, even). Think I will stick to the idea of doing some basic stuff in C, but adding game logic in lua. Now on to that movement prediction et al! Links A short video, showing the whole mess in action: Link to executables (server+client): http://www.mikoweb.eu/tmp/LWEN_Networking_Exe.zip (10MB) Link to Source (VS2013+LW): http://www.mikoweb.eu/tmp/LWEN_Networking_Source.zip (25MB) Pictures The test app (here, 3 clients and the server running on Win)
  4. Rather than creating a new topic, I feel free to jump in here with a related question. Anyone got objects (Vec3, in my case) passed between lua and C? For e.g. int types, I would do like (calling from lua into C) lua_register(Interpreter::L, "CalledFromLua", CalledFromLua); extern "C" int CalledFromLua(lua_State* L) { int i = lua_tointeger(Leadwerks::Interpreter::L,1) CallMyFunc(i); return 0; } and (calling from C into lua) void CalledFromC(int i) { lua_getglobal(Leadwerks::Interpreter::L, "AFunction"); if (!lua_isfunction(Leadwerks::Interpreter::L, -1)) { lua_pop(Leadwerks::Interpreter::L, 1); return; } lua_pushinteger(Leadwerks::Interpreter::L, i); if (lua_pcall(Leadwerks::Interpreter::L, 1, 0, 0) != 0) { printf("Callback_ClientPosRot ERROR: %s\n", lua_tostring(Leadwerks::Interpreter::L, -1)); return; } } But with e.g. Vec3, this seems to be a bit more complicated. Like, using 'userdata' or such things. Any hints?
  5. Just for the records: I have seen a similar effect as tinyboss.
  6. Nothing really new - but anyway, these are my key points: - the C++ API (hell, I still can't be arsed to dive into lua) - full shader access - helpful and responsive community - regular dev presence in forums
  7. Wonderful landscape, grats! Say, could you elaborate a bit more on how you made the terrain, the vegetation and such? Or, maybe, did you do so already someplace else? Thanks a lot.
  8. Thanks for your comments. Ah, I see - yeah, keyframe animation is the word. So, I will try to rig that thing and then see how it goes. For clarification: Yes, I did export with animation and that could be interpreted by UU3D. I did not export again from UU3D and try with LW then afterwards, tho.
  9. Hi Folks, I have set up a very basic animated model in C4D (R15 in my case) and exported to fbx. It is just a cube spinning on a platform (no rigging or whatnot). Upon LW import, the animation does not show up, tho. It imports well into UU3D and others. The fbx can be found here: http://www.mikoweb.eu/tmp/LW_Testmodel.zip Anyone got such animation into LW directly from C4D (R15 in my case)? (I know I could/should try using e.g. UU3D or Blender as "middleware", but would love to get it in directly...) Thanks for your comments.
  10. Think I have the same problem here. After exiting LW, I can't close Steam, as it thinks LW is still active. Also, I get the "already running" message on restarting LW.
  11. At least for Windows, I assume LW is using the Win API ShowCursor function. See here: http://msdn.microsoft.com/de-de/library/windows/desktop/ms648396%28v=vs.85%29.aspx This function actually uses a counter, not just switching the cursor on/off. Starting with 0 = mouse ON (!). Counting through your code: Program Start: 0 Call to ShowMouse: 1 Call to Hidemouse: 0 Call to ShowMouse: 1 Call to HideMouse: 0 As Windows hides the mouse with counter -1 and below, it is never hidden - I think. If this really is the case, the LW docs might need a bit of updating...
  12. Actually, I have a stub dll test case in place now. Got me the SDK and linked it with a dll which in turn is loaded dynamically at runtime now (or, well, not - as one prefers). During testing, I noticed that the steam.dll keeps rather passive, as long as the SDK's SteamAPI_Init() isn't called. So, to me, it seems Leadwerks invokes the SteamAPI_Init() not only during a call of Steamworks::Initialize (what I was assuming), but always. If this is correct, some remedy could be achieved by moving the call to Steamworks::Initialize and only execute on demand. No need to fiddle with steam_appid.txt etc then, if a user doesn't call Steamworks::Initialize anyway... my 2 cents
  13. Just for the fun of it, I played with this dynamic loading idea a bit more. My current WIP can be seen here: http://www.mikoweb.eu/tmp/LoadSteamDll.cpp (it is a console app already retrieving several of the dll's entry points - yet not all). By now, I believe that directly loading the dll and getting all the proc addresses run-time is not really the way to go. To get all the interfaces and inline code straight, one would probably need to include many parts of the steam header files - which is doubtable in terms of copyright imo. Also, it would be a mess when the Steam SDK is being updated. What I currently think about is a stub dll, compiled with the Steam SDK as-is. Now this dll would only be loaded run-time if Steamworks::Initialize() is being called. And it would only expose the Leadwerks Steamworks functions. After all, LW is encapsulating the Steam API anyway... Just FYI...
  14. As I understand, Run-Time Dynamic Linking might do the trick. Only load the dll if Steamworks::Initialize() is being called. No need to distribute the steam dll with the engine at all if someone's app doesn't use steam. All the dll's exposed function call addresses need to be manually retrieved from the dll at runtime, tho (DependencyWalker shows about 59 of them)... http://msdn.microsoft.com/en-us/library/windows/desktop/ms686944%28v=vs.85%29.aspx
  15. Hi there, I just got my Leadwerks Std. updated through Steam (stable branch). My existing project needed to be updated through the Project Manager -> Update function. After doing so, I noted the Start.map (where I had added my custom objects) was back to a default state. My objects were gone. To recreate: Add an "old" Start.map with custom objects to an already converted project, run Project Manager->Update (it shows "1 file updated"), then check the new Start.map. I believe this is a bug - as it seems to corrupt the updated project? Thanks for advice miko
×
×
  • Create New...