Tasharen Entertainment Forum
Support => TNet 3 Support => Topic started by: MCoburn on September 21, 2014, 07:24:25 PM
-
Hi,
I'm one of the two guys working at [in]Sanity Interactive, a co-op game development crew that is currently working on a shooter project, which will feature both dedicated server and private matches where the player hosting the match is able to play as well. We have been using Unity's built-in framework (RakNet) and we've had troubles trying to get the network layer to work correctly when sending RPCs back to clients so they can load the right level.
I was wondering if you could confirm if TNet has server-instructed level loading capabilities, and if you could post a snippet of code that would be the basis of the server telling the client what to load.
When the player connects to the server hosting the game, before the client should do anything, the server sends a message (let's for arguments sake say "clientLoadLevel") to the client, with a string of it's level that is currently loaded (called from Application.levelLoadedName, etc). The client must then accept this message and start loading the level (I see TNet has TNet.LoadLevel which is nice!) otherwise I intend to kick the client saying "Wrong level loaded" to prevent really weird stuff happening.
So, in pseudo code:
Server: Client connects > Client, clientLoadLevel("mp_testlevel") > *waits for client response * > Okay, you've loaded, welcome to the game
Client: Connect to server from mainmenu > Server, what is the level? > Okay, I'll load mp_testlevel > Yay!
The thing here, is that in our current Unity RPC implementation, is that the RPC gets fired in a different scene since the server has loaded the level (let's say mp_testlevel) and since the client is on the main menu, it freaks out and errors or doesn't load the level and weird stuff starts happening.
So, this is one of the things I'm looking at in TNet. Plus, for shooters, I need a robust and reliable solution because I don't want major network glitches due to bugs in Unity's set-up. I realize that TNet is only the networking cake, and my game is the icing on top of that, but if you can confirm I can do the level loading as shown above, then you pretty much have a purchase.
Cheers!
-
By default TNet's server is just an echo server, for the most part. You send messages to it with certain flags, and it will decide what to do with them. It may forward them to all players, to one player, or do something with the message and send out a different response.
The first player to join a channel is that channel's "host" (think channel operator in IRC terms), and you can check this value on the client side (TNManager.isHosting) to determine if certain logic should be executed or not (AI scripts for example).
The server doesn't run any game-specific logic by default, and it's not advisable to do so. You should be doing this on clients. In your example, this is how I'd do it:
1. Client 1 connects, and with TNet you specify which scene to load on a successful connection (for example scene "First").
2. Client 1 receives a "joining channel" notification, along with the scene to load ("First"), and loads the scene (done automatically).
3. Client 1 gets OnNetworkJoinChannel(true, null) -- the channel has been joined successfully and the scene was loaded.
Note how everything is done on the client, it merely goes through the server? The server validates everything and sends the corrected messages. Examine the next client:
1. Client 2 connects and chooses level "Abc123" as the desired scene for TNManager.JoinChannel.
2. Client 2 receives a "joining channel" notification, along with the scene to load ("First", not "Abc123", because the first player created the channel, not this one!), loads the scene.
3. Client 2 gets OnNetworkJoinChannel(true, null).
So although the second client requested a different scene, the first player's choice is what gets sent back to him.
-
Thanks for the quick response. This clears the confusion a fair bit. ;)
If I am not mistaken, does TNet automatically reply to client 2's joining notification with the level ID when client 1 is hosting? Or would I have to fire a RFC or custom packet to client 2?
-
Level names, not IDs -- but yes. For other notifications, it depends on the notification. TNet will always send all created objects and RFCs you marked as "saved" before the join operation completes (Target.AllSaved, Target.OthersSaved). TNet won't send any RFCs you marked as not saved (Target.All, Target.Host, Target.Others).
-
Thank you for confirming. Well, you have a purchase!
So, when clients connect to the server, will they automatically be created on the client-side? I remember there was a "Auto Create" script going back a few builds that would auto create objects in the scene when it loaded?
-
What I do in Windward is in my game scene there is a game object with TNAutoCreate on it that will create an instance of the player. So as soon as the player joins, he will create an instance of his own avatar, basically.
-
How would one go about putting a splash screen with a preview of the level before TNet actually joins the channel and loads the specified level?
At the moment, I have "server and client" functionality working - you select your server parameters, and then hit start and TNet boots up.
This is no biggie for the host. I can just make a toggle that will overlay the "Loading..." screen with a level image and then use TNet.LoadLevel("blah");
However, on the client, we connect to the server, then join channel 1 (which is the actual game channel). However, unless we poll the ChannelList first so we can get the level parameter from the channel that the game is happening on, the client will just "freeze" on whatever was there in the scene until the level is done loading.
Is it possible to get a channel's data like TNManager.client.RequestChannelInfo(id of channel) ? This would be really beneficial, because I can fetch the level out of the returned info, display the respective splash screen and then let TNet do the loading.
Or do you have another idea?
-
Join a dummy channel first, then Application.LoadLevelAdditive the actual level.
In Windward I don't even generate the actual level beforehand. Levels are procedural, so all I have in my scene is the UI, open to the loading screen by default. So the first thing users see when they join that channel is the loading screen, which then proceeds to do procedural generation in the background over the course of multiple frames.
-
So, you're saying create the server, then join a channel with the respective ID (let's say 1) and the levelname of say "mp_superflat_loader" that contains the UI overlay of the loading screen and the level loading script. TNet will load that level, which will start the actual proper level loading process, which level is "mp_superflat_real".
When the level loading isDone flag is true, then the script triggers the "OnLevelWasLoaded" call to spawn the client-side player manager, destroys itself, and then the player has it's control ?
Would I need to tell TNet to pause network queue processing before I start loading? Or will TNet detect I'm loading the level and auto-pause itself?
-
TNet doesn't track level loads you do yourself.
1. When you want to join a channel, first Application.LoadLevel your scene with the loading screen. Set this screen's game object to be DontDestroyOnLoad, and proceed to TNManager.JoinChannel to your proper game scene.
2. When you get OnNetworkJoinChannel, fade out your loading screen, then destroy the game object you marked as DontDestroyOnLoad.
-
Uh.... double quote, wth? :o
TNet doesn't track level loads you do yourself.
1. When you want to join a channel, first Application.LoadLevel your scene with the loading screen. Set this screen's game object to be DontDestroyOnLoad, and proceed to TNManager.JoinChannel to your proper game scene.
2. When you get OnNetworkJoinChannel, fade out your loading screen, then destroy the game object you marked as DontDestroyOnLoad.
Alright, I took your advice and it worked as intended.
When I connect to the server (in this case, a in-game hosted one), I immediately load a dummy scene (mp_superflat_load) with the splash image and invoke joining the channel that loads the correct level (mp_superflat). There is a little delay as Unity loads the level. When the channel is joined, it sets a flag, eases out the splash image which reveals the level and everything under that. Let's just hope it doesn't bug out. :P
Now with this in mind, I can focus on player controls and Network Sync...
-
The server doesn't run any game-specific logic by default, and it's not advisable to do so. You should be doing this on clients.
Do you mean this in general, or for this specific scenario? I hate to hijack the thread but I was curious as to why you recommend not doing logic on the server with TNet.
I was under the impression I could build an authoritative server with TNet that ran a game simulation and updated each client. The clients could worry about prediction, rendering, gathering input to change the simulation.
-
Do you mean this in general, or for this specific scenario? I hate to hijack the thread
No offense taken. You raised a valid point.
but I was curious as to why you recommend not doing logic on the server with TNet.
I was under the impression I could build an authoritative server with TNet that ran a game simulation and updated each client. The clients could worry about prediction, rendering, gathering input to change the simulation.
My setup that I'm hoping to implement is sorta semi-authoritative. The server will run the prediction when you shoot your weapon, and report back to the client that request the shot "that's a hit on X", "that's a hit on Y", "no, you missed Z".
The problem with authoritative, is while the server is the master and controls everything, that's a lot of work for the server to do. If you have the CPU, RAM and knowledge then sure, you can do it - TNet is just a cake and you can put whatever icing you want on top. In my setup, I prefer to have the clients be "dumb" to some extent, but when you fire the weapon, the hosting player has the final say.
The host handles the Score Board and weapons. The connecting clients just report to each other where everyone is and what they are doing (moving, dead, idle ... etc). One thing we also need to do is Entity Rewinding... to help compensate for lag/jitter.
-
@nzizazzo: It's better for you as a developer. 1000 clients means 1000 work-horses for you to utilize, instead of having your poor server do 1000 times more work. The more you offload onto your clients, the better off for you, and the more clients you will be able to support.
P.S. Where are you working at nowadays, Nick?
-
My setup that I'm hoping to implement is sorta semi-authoritative. The server will run the prediction when you shoot your weapon, and report back to the client that request the shot "that's a hit on X", "that's a hit on Y", "no, you missed Z".
The problem with authoritative, is while the server is the master and controls everything, that's a lot of work for the server to do. If you have the CPU, RAM and knowledge then sure, you can do it - TNet is just a cake and you can put whatever icing you want on top. In my setup, I prefer to have the clients be "dumb" to some extent, but when you fire the weapon, the hosting player has the final say.
The host handles the Score Board and weapons. The connecting clients just report to each other where everyone is and what they are doing (moving, dead, idle ... etc). One thing we also need to do is Entity Rewinding... to help compensate for lag/jitter.
What level of cheat-prevention are you aiming for in your project? Because with the strategy you outlined, I'd imagine it'd be fairly easy for clients to misreport their location to peers, and cause some major issues. Doing hit / collision checking on one host should avoid major score / state synchronization issues, but wouldn't a client misreporting it's position cause problems? Client A could report it's position falsely and make it to be nearly impossible for clients B, C, and D to hit client A between the update on their screens and the roundtrip back to the "host".
I ask because I feel like your end-goal is the same as my own, and am at the point where I have to make smart architecture decisions. It's pretty desirable to have the server doing as little as possible, but having no cheating is important too.
@nzizazzo: It's better for you as a developer. 1000 clients means 1000 work-horses for you to utilize, instead of having your poor server do 1000 times more work. The more you offload onto your clients, the better off for you, and the more clients you will be able to support.
Agreed, having the server do as little as it can is the best since you don't have to have a powerful server behind it, but how would one strike a good balance between keeping the game cheat-free, and having work offloaded to each client?
I see two extremes: one end of the spectrum is having clients do everything, but that is the worst-case scenario for cheat prevention and best for resource utilization. The opposite is having a server do everything, which is the best case scenario for cheat prevention, but terrible on resource utilization. I realize it's really dependent on the game, but is semi-authoritative generally the balanced option?
Have you ever written anything about your networking strategy for Windward? I wouldn't mind reading through something like that :). It might even be a good piece to convince more people to use TNet.
P.S. Where are you working at nowadays, Nick?
I'm taking a break from games to make some money, haha... I'm with The Toronto Star / Metroland right now doing mobile development. I'm working on some game side projects though, to stay sane (in fact, I just picked up TNet for the project!).
You working at Unity still?
-
What level of cheat-prevention are you aiming for in your project? Because with the strategy you outlined, I'd imagine it'd be fairly easy for clients to misreport their location to peers, and cause some major issues. Doing hit / collision checking on one host should avoid major score / state synchronization issues, but wouldn't a client misreporting it's position cause problems? Client A could report it's position falsely and make it to be nearly impossible for clients B, C, and D to hit client A between the update on their screens and the roundtrip back to the "host".
I ask because I feel like your end-goal is the same as my own, and am at the point where I have to make smart architecture decisions. It's pretty desirable to have the server doing as little as possible, but having no cheating is important too.
Honestly, I'm not quite sure what implementation I should go with. I just don't have the finances to afford game servers that have big capacities of CPU grunt and RAM which is annoying, but as a 2-man start-up you have to do what you have to do. Even if I did have the finances, I would be spending it on team development hardware and software licensing, plus business costs.
What I can see is the "host" is the one that dictates what's going on, as for the match type, scoreboard, timer, etc. When a weapon requests to fire, it (the host) does the estimation and all the related things. I could view this as a "Thin Client Network" in the IT sector. However, as Aren stated, it's better to let the clients do all the other work, rather than just be a "puppet". Then another factor is client correction - the server needs to be able to tell a client "hey, you goofed, get back to Vector3(x,y,z)".
I really don't know. I think it's best if I can get together with someone who has worked on network systems before and discuss it with them, otherwise I'm going to run around in circles and goof something up. Cheating may happen, but hopefully kits like Anti-Cheat Toolkit will help protect the core, but not eliminate all the vectors. I'll possibly rely on community reporting, and have a function assigned to a snap key that will take a picture of whatever's on screen and relay it to the team with a message for review. I will, however say that if the players are found to be cheating with enough evidence (Aimbot, etc) they will be banned for oblivion (however I implement this will be a discussion topic down the track).
I don't mind having another brain (;)) to help assist with game coding areas that I am not fluent in.
I see two extremes: one end of the spectrum is having clients do everything, but that is the worst-case scenario for cheat prevention and best for resource utilization. The opposite is having a server do everything, which is the best case scenario for cheat prevention, but terrible on resource utilization. I realize it's really dependent on the game, but is semi-authoritative generally the balanced option?
I'd love to see what Aren weighs in with.
-
@Nick: Naw I left Unity almost a year ago. I've focused on NGUI for the first couple of months after that, and now I'm back to game dev full time. Windward is going to be out on Steam shortly.
@MCoburn and @Nick: As I always say make a game worth hacking first, and then worry about hackers. Preventing hacking is like preventing piracy. You can try, but you will fail. You can make it less convenient for them, sure -- but you won't prevent them from getting the results they want in the end. Also keep in mind that some games actually benefit from a lack of such features. Anything you want moddable is better left without server-side checks for example.
-
@MCoburn and @Nick: As I always say make a game worth hacking first, and then worry about hackers. Preventing hacking is like preventing piracy. You can try, but you will fail. You can make it less convenient for them, sure -- but you won't prevent them from getting the results they want in the end. Also keep in mind that some games actually benefit from a lack of such features. Anything you want moddable is better left without server-side checks for example.
That rings true. I guess hackers won't want to hack a game that isn't deemed worthy of hacking. I spoke to my teammate and we agreed that there's always going to be a minority of the public who want to flex their hacking skills, either for fun against there hacker friends, or just script kiddies.
At the very least, Unity's runtime has some sort of protection. I'm not sure how well that protection works, but it's not using a engine, like say IDTech where it's rather easy to hook up an aimbot. Whatever the case, I think for now I focus on making the game happen, then if the hacks occur, see what vectors they are attacking and then revise that chunk of the code. Like I said, we can't have the "perfect" app that is 100% hacker proof, somewhere there's bound to be a guy/girl poking memory and looking through the code to see what is going on.
I will protect the vitals though, such as health and such.