Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - cmifwdll

Pages: 1 ... 15 16 [17] 18 19
241
TNet 3 Support / Re: Sync player states and animations
« on: May 27, 2016, 02:44:17 AM »
Sorry, you don't have to use bitpacking.
Fixed up the relevant parts for you:
  1. IEnumerator SyncInput()
  2. {
  3.         while(TNManager.isInChannel)
  4.         {
  5.                 // note: most Input.Getblahblah calls should be made in the Update() function, GetAxis is the exception.
  6.                 short horizontalInput = Input.GetAxis("Horizontal").ToShort();
  7.                 short verticalInput = Input.GetAxis("Vertical").ToShort();
  8.                 tno.Send(NetworkManager.NETID_SYNC_INPUT, Target.OthersSaved, horizontalInput, verticalInput);
  9.                 yield return new WaitForSeconds(1f / PacketsPerSecond);
  10.         }
  11. }
  12.  
  13. [RFC(NetworkManager.NETID_SYNC_INPUT)]
  14. public void ReceiveInput(short horizontal, short vertical)
  15. {
  16.         float horizontalInput = horizontal.ToSingle();
  17.         float verticalInput = vertical.ToSingle();
  18.         // set the CharacterController's values according to the input received
  19. }
  20.  

But to answer your question, packedInputs <<= 16; is the same as writing packedInputs = packedInputs << 16;
The "<<" operator in this context shifts packedInputs to the left 16 bits.
  1. byte packedByte = 9;
  2. // packedByte bits:
  3. // 00001001
  4. // if you were to do Debug.Log(packedByte.ToString()) the output would be 9.
  5. packedByte = (byte)((int)packedByte << 4);
  6. // packedByte bits:
  7. // 10010000
  8. // if you were to do Debug.Log(packedByte.ToString()) the output would be 144.
  9. // you can then fit another value between 0-15 into packedByte
  10. // inserting the value via the OR operator
  11. packedByte = (byte)((int)packedByte | 6);
  12. // packedByte bits:
  13. // 10010110
  14. // if you were to do Debug.Log(packedByte.ToString()) the output would be 150.
  15. // now you have two values stored in a single byte: 9 and 6.
  16.  
More info on MSDN: https://msdn.microsoft.com/en-us/library/a1sway8w.aspx

In case you were confused by the |= operator as well, that's a bitwise OR.
The wikipedia article explains the bitwise operators really well: https://en.wikipedia.org/wiki/Bitwise_operation#OR

But the relevant part of my previous post stands: Send your inputs instead of your results. Receive the inputs and calculate the results on each client. With this, handling animations should be literally the exact same as handling it on the local player. Absolutely no distinction between the two: no extra code, nada.

242
TNet 3 Support / Re: Sync player states and animations
« on: May 26, 2016, 06:33:28 PM »
You're sending an RFC in Update, that's going to wreak havoc on your PC and at every hop between you and your server. Don't do that ever.
Also, you're sending your full position and rotation each time, this is exactly against Aren's advice.
I think this is more inline with what Aren always talks about w/ syncing inputs:
  1. public static class ExtensionMethods
  2. {
  3.         public static short ToShort(this float value)
  4.         {
  5.                 return (short)UnityEngine.Mathf.Round(value * short.MaxValue / 1000);
  6.         }
  7.  
  8.         public static float ToSingle(this short value)
  9.         {
  10.                 return (float)((double)value) * 1000 / short.MaxValue;
  11.         }
  12. }
  13.  
  14. public class SyncMovement : TNBehaviour
  15. {
  16.         [SerializeField]
  17.         float PacketsPerSecond = 10f;
  18.        
  19.         void Start()
  20.         {
  21.                 StartCoroutine(SyncInput());
  22.         }
  23.        
  24.         void Update()
  25.         {
  26.                 // probably do some animation stuff here according to the CharacterController
  27.         }
  28.        
  29.         IEnumerator SyncInput()
  30.         {
  31.                 while(TNManager.isInChannel)
  32.                 {
  33.                         // note: most Input.Getblahblah calls should be made in the Update() function, GetAxis is the exception.
  34.                         int packedInputs = 0;
  35.                         packedInputs |= Input.GetAxis("Horizontal").ToShort();
  36.                         packedInputs <<= 16;
  37.                         packedInputs |= Input.GetAxis("Vertical").ToShort();
  38.                         tno.Send(NetworkManager.NETID_SYNC_INPUT, Target.OthersSaved, packedInputs);
  39.                         yield return new WaitForSeconds(1f / PacketsPerSecond);
  40.                 }
  41.         }
  42.        
  43.         [RFC(NetworkManager.NETID_SYNC_INPUT)]
  44.         public void ReceiveInput(int packed)
  45.         {
  46.                 short verticalCompact = (short)(packed & short.MaxValue);
  47.                 packed >>= 16;
  48.                 short horizontalCompact = (short)(packed & short.MaxValue);
  49.                 float verticalInput = verticalCompact.ToSingle();
  50.                 float horizontalInput = horizontalCompact.ToSingle();
  51.                 // set the CharacterController's values according to the input received
  52.         }
  53. }
  54.  

It's not complete, but the idea is there at least. Send your inputs instead of your results. Receive the inputs and calculate the results on each client. With this, handling animations should be literally the exact same as handling it on the local player. Absolutely no distinction between the two: no extra code, nada.

Note: I've found this method of float compression to be accurate from the hundredth (0.01f) to the hundreds (999f); this can be tweaked. You don't need to use float compression or bit packing, but I'm in a show-off mood today :P

243
TNet 3 Support / Re: Tnet 3.0 send to other player [RFC]
« on: May 26, 2016, 01:36:26 PM »
I wouldn't use RFC(0), but instead would suggest just using the function's name instead. Also note that collider.GetComponent implies that TNObject is on the collider. Generally it's on the rigidbody instead, so it would be collider.GetComponentInChildren<TNObject>() instead.

Wellll, if we're going to nitpick, I'd say using the function's name is less efficient than a byte identifier. For one, using the ID reduces packet size (doesn't have to write the string). Secondly, when the packet arrives and execution occurs, it's less expensive to look up the ID than the name:
  1. if (ent.id == funcID)
  2. generates the following IL:
  3. ldloca.s CachedFunc
  4. ldfld System.Byte CachedFunc::id
  5. ldarg.1
  6. bne.un -> 49
  7.  
Compared to looking up the name:
  1. if (ent.func.Name == funcName)
  2. generates the following IL:
  3. ldloca.s CachedFunc
  4. ldfld System.Reflection.MethodInfo TNet.CachedFunc::func
  5. callvirt System.String System.Reflection.MemberInfo::get_Name()
  6. ldarg.1
  7. call System.Boolean System.String::op_Equality(System.String, System.String)
  8. brfalse -> 52
  9.  

String comparisons are always going to be more expensive than simple integer comparisons.

Granted, using a string adds a lot to convenience and readability, but you can do something like so to achieve similar results:
  1. public class NetworkManager
  2. {
  3.     public const byte NETID_COLLISION_OTHER = 1;
  4. }
  5. tno.Send(NetworkManager.NETID_COLLISION_OTHER, Target.All, otherTNO.ownerID);
  6.  
  7. [RFC(NetworkManager.NETID_COLLISION_OTHER)]
  8. public void RFCCollisionOther(int id)
  9. {
  10.     if (id == tno.ownerID)
  11.     {
  12.         // do stuff
  13.     }
  14. }
  15.  

Additionally, why stop at checking just children? Use the following extension method instead:
  1. public static T GetComponentAll<T>(this GameObject go) where T : Component
  2. {
  3.         T component = go.GetComponent<T>();
  4.         if (component == null)
  5.         {
  6.                 component = go.GetComponentInParent<T>();
  7.                 if (component == null)
  8.                 {
  9.                         component = go.GetComponentInChildren<T>();
  10.                         if (component == null)
  11.                                 return null;
  12.                 }
  13.         }
  14.         return component;
  15. }
  16.  

Though, TNObject does call FindParent in its Start() function, so you only really need to check downwards. Also, you were probably more suggesting to not use 0 in tno.Send as SendRFC checks if byte ID is 0, but that's more of a design flaw in my opinion. Shouldn't it check if id is -1 instead?

244
TNet 3 Support / Re: Tnet 3.0 send to other player [RFC]
« on: May 25, 2016, 12:33:02 PM »
  1. void OnTriggerEnter(Collider collider)
  2. {
  3.     TNObject otherTNO = collider.GetComponent<TNObject>();
  4.     if (otherTNO == null)
  5.         return;
  6.     tno.Send(0, Target.All, otherTNO.ownerID);
  7. }
  8.  
  9. [RFC(0)]
  10. public void SomeRFC(int id)
  11. {
  12.     if (id == tno.ownerID)
  13.     {
  14.         // do stuff
  15.     }
  16. }
  17.  

245
TNet 3 Support / Re: Seperate Players?
« on: May 25, 2016, 12:25:44 PM »
So, because TNet has to work with everything the server is very "dumb" by default. It has no knowledge of Unity, what's going on in the game, or where objects are in the game. It doesn't even know what game objects are. The server functions as a relay. It receives a packet from a player and sends it to (an)other player(s). So if you want the server to control loot spawning and all that, you'll need to build onto the server. It's a complicated process, but thanks to TNet being very extensible it's made a lot easier. Still, it requires in-depth knowledge of networking, Unity, and TNet as a whole. However, if you're okay with settling for something much easier but less server-sided, you can have the host of the channel control loot spawning. For that it's as simple as checking TNManager.isHosting. So your loot spawn function might look like:
  1. void SpawnLoot()
  2. {
  3.     if (!TNManager.isHosting)
  4.         return;
  5.     TNManager.Instantiate(params go here);
  6. }
  7.  

Moving on, the server name is available when retrieving it from the lobby server. It's contained in the ServerList.Entry class. Seems you can access it from TNLobbyClient.knownServers as well. You could also set the name of the server in the channel data, or the serverOptions, or send it as a custom packet. Possibilities are limitless.
TNServerInstance shouldn't even be available if you're connecting as a client to a remote server. If you're trying to get the name of the server AS the server, then TNServerInstance.serverName should work, otherwise you can access it directly through an instance of GameServer.name.

The high ping sounds like an issue with your VPS host. Try opening up command prompt (or terminal) and pinging your VPS. If it's high then complain to your host. Note that distance effects ping.

I don't know about the lobby server issue - that string doesn't even exist in TNet2 (I haven't upgraded to TNet3 yet :() - so Aren or someone else will have to answer that one for you :)

246
TNet 3 Support / Re: Seperate Players?
« on: May 24, 2016, 01:07:28 PM »
so I have to make 2 different scripts for EVERYTHING on my player?

Nope.

Say I have a fully complete and working First Person Controller with weapons and everything, would I have to remake EVERYTHING to make it work over the network?
or can i just build ontop of what i already have?

At the top of the FirstPersonController.cs file you should see:
public class FirstPersonController : MonoBehaviour
Simply change this to:
public class FirstPersonController : TNBehaviour
and now it's recognized and compatible with TNet.

The tutorials and documentation should clear things up and give you a good starting point. There's also a wealth of information on this forum, so don't be afraid of the search button :)

247
TNet 3 Support / Re: Seperate Players?
« on: May 23, 2016, 05:41:49 PM »
Every networked object should have a TNObject component attached, and any script utilizing it should inherit from TNBehaviour (not MonoBehaviour). From there, simply use the tno.isMine property. Note that TNBehaviour itself derives from MonoBehaviour, so anything you need from MonoBehaviour will still be there.

I'd suggest checking out the tutorials:
http://www.tasharen.com/forum/index.php?topic=13953.0
And the video Aren made:
https://www.youtube.com/watch?v=7oBhEwAHU5w

Seems TNet3 comes packaged with documentation in the form of pdf files as well, so check those out too.

248
TNet 3 Support / Re: Unable to open port
« on: May 23, 2016, 01:35:08 PM »
UPnP is just a protocol that gives applications the power to open ports automatically instead of you having to do it manually. It isn't necessary for the server to work, or for hole-punching to work either. Not every router even supports UPnP, so this message is perfectly normal.

If you're having trouble connecting to the server, make sure the ports are open on both your OS and your router. Other than that, it's possible that your client code is the problem. If your server and client are on the same PC (or local network, even) make sure you're connecting using loopback address or your local address, *not* your public address. Most routers don't support hairpinning.

249
TNet 3 Support / Re: TNet 3 - Suggestion or Explanation
« on: May 21, 2016, 04:10:51 PM »
TNet wraps execution of RFCs in a try-catch block. You can see this (in TNet2 at least, probably the same in TNet3) in TNUnityTools.cs @ ExecuteAll(...) function.

When an exception occurs, it's caught by that block and printed out accordingly. This is why it'll always show as a "TNet error", even though TNet is just reporting the error. TNet does report the actual exception, along with the stack trace, you just have to scroll down a bit.

If you'd like to change this behaviour just modify the PrintException(...) function in TNUnityTools.cs, or remove the try-catch block in ExecuteAll(...) if you'd like to handle exceptions yourself (not recommended).

250
TNet 3 Support / Re: HTTP GET requests
« on: April 09, 2016, 07:14:22 AM »
Apache is *A* webserver but it is not *THE* webserver. Think of this feature as making TNet a very, very, very stripped down version of Apache. This feature makes TNet its own webserver (though very limited, don't expect to host websites with it :p)

I'm no web programmer and have very little experience in this area, but you could do something like this:
test.php:
http://pastebin.com/RDDGHPdk (had to upload to pastebin as html tags aren't allowed on this forum, even when enclosed in bbc code tags)

This would create a button that - when clicked - will send the "Shutdown=1" message to the TNet instance running on 127.0.0.1:5127.
You would then have to write some code on TNet's side to handle this message. I don't have TNet 3 so I can't provide an example for that, but maybe someone else can.

You can either install php (w/ curl) on your own computer and run test.php from your web browser, or you can upload test.php to a web host (that has php and curl support already) and use it from any computer.

This featue is useful because it enables you to build a web interface to remotely administrate your TNet server. It could serve many other uses as well if you get creative with it, such as having a website w/ a live leaderboard or a live feed of what's happening in your game.
Also, System.Net isn't available on mobile devices, right? So this feature could make TNet usable on mobile devices assuming it doesn't use the System.Net namespace. Also, you'd have to flesh it out a bit to add support for POST requests as GET isn't suitable for sending a lot of data, but still, it'd be a big step forward.

251
TNet 3 Support / Re: Some Questions
« on: February 29, 2016, 10:36:16 AM »
1. Grossly simplifying, TNet is basically a relay server (think like a radio tower relaying a signal). All physics / logic is handled on the client, and the results are sent to the other players through the server.

2. Depends. Aren has shared some stats from hosting his Windward servers, "I had 316 players connected to my home PC using 200 MB RAM and 4.5% CPU [with a 20 Mbit upstream connection]"
(http://www.tasharen.com/forum/index.php?topic=13077.msg59083#msg59083 && http://www.tasharen.com/forum/index.php?topic=13482.msg60517#msg60517).
I think resource usage could be reduced even further with the changes to DataNode in TNet 3, though I haven't done any testing myself.

3. If you have to ask, unequivocally no. TNet isn't authoritative. You don't want clients handling physics or AI logic in an MMO, do you? You could make some easy modifications to make it so the "host" (has that been refactored to "operator" yet?) is the only one calculating important stuff, but that's still not truly authoritative. The server has no clue what's happening in the world: it's only blindly passing data. This isn't to say TNet is bad in any way. TNet is brilliant in many regards and I highly recommend it, but I believe an MMO - by the classic definition - requires its own specialized networking solution.

252
TNet 3 Support / Re: TNManager.isHosting always returning true
« on: February 05, 2016, 04:25:44 PM »
You shouldn't be doing that in Awake(). Awake gets called as soon as the object is loaded. So if your object's Awake gets called before TNet's Awake then you'll run into this issue. Awake should only be used for initializing that specific gameobject (without referencing any other object). Consider using Start() if you need to initialize your object based on another object.

Assuming you're putting this object in the scene before running the game, putting this code in Start won't work either, because Start gets called immediately after the last Awake call, and it's impossible that you'd be able to connect by then.

Try using the OnNetworkConnect(bool result, string message), OnNetworkJoinChannel(bool result, string message), or OnSetHost(bool hosting) events. For the latter, you'll have to subscribe to the event like so:
  1. void Start()
  2. {
  3.     TNManager.client.onSetHost += OnSetHost;
  4. }
  5.  
  6. void OnSetHost(bool hosting)
  7. {
  8.    
  9. }
  10.  

Alternatively, you can just delay the creation of the TargetSpawnControl gameobject until TNet has loaded, then TNManager.isHosting should work properly in all cases.

253
TNet 3 Support / Re: if(TNManager.isThisMyObject) error message
« on: February 04, 2016, 06:42:40 AM »
I don't have TNet 3, but try tno.isMine instead of TNManager.isThisMyObject. Remember to derive from TNBehaviour.

In TNet 2, TNManager component can be added from Add Component > TNet > Network Manager. Not sure if this has changed in TNet 3.

I think TNet 3 is still technically a beta, so I imagine there'll probably be better documentation / updated tutorials upon its official release.

254
How can I use the TNManager.serverTime to know when Player A triggered his skill for Player B?

  1. [RFC(NET_ID.SkillTrigger)]
  2. public void Net_SkillTriggered(SkillType skill, long timestamp)
  3. {
  4.     DateTime forhumaneyes = new DateTime(timestamp);
  5.     Debug.Log("Skill " + skill.ToString() + " triggered at " + forhumaneyes.ToString());
  6. }
  7.  
  8. public void CastFireball()
  9. {
  10.     tno.Send((byte)NET_ID.SkillTrigger, Target.All, SkillType.Fireball, TNManager.serverTime);
  11. }
  12.  

It's been a while since I've wrote any code but you get the idea ^.^

I think you're approaching it the wrong way though. You shouldn't be designing your game around a 500ms ping. That is incredibly high even for players on opposite sides of the planet, and adding a timestamp to the packet isn't going to do much because you can't travel back in time. Not to mention a timestamp can only be so precise.

My head is loopy right now so maybe Aren can provide some design advice. If not then you could look up deterministic lockstep. I don't think your problem would require such a solution, but the principle could be helpful. Some interesting words thrown together: "playout delay buffer". As summarized by Glenn Fiedler at this page, "[I]n short, what you want to do is buffer packets for a short amount of time so they appear to be arriving at a steady rate even though in reality they arrive somewhat jittered".

Note: Not sure if links to external sites are allowed. My apologies if they are not. I felt the need to cite the author of the quote for moral reasons.

255
TNet 3 Support / Re: TNET allias , admin kick and ban
« on: December 10, 2015, 08:10:01 AM »
If you can tell us your OS you'll receive more specific help.

You need to place admin.txt in your Documents folder in some configuration:
By default (applicationDirectory not set): C:\Users\YOURUSERNAME\Documents\ServerConfig\admin.txt
If applicationDirectory is set: C:\Users\YOURUSERNAME\Documents\<applicationDirectory>\ServerConfig\admin.txt
If you launch with -localPath set: <Wherever the EXE resides>\ServerConfig\admin.txt

Now, all paths containing the following are !!OS-dependent!!: \Users\YOURUSERNAME\Documents.
So you'll need to google "C# My Documents path <YOUR OPERATING SYSTEM>" if you don't know it.

The line of code that determines this is: System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
You can find it by searching "static public string persistentDataPath" in TNTools.cs

If you really need our help, do us a favor and paste the following code in your project and tell us the output:
  1. void Start()
  2. {
  3.         string documentsPath = "";
  4. #if UNITY_ANDROID || UNITY_IPHONE || UNITY_WEBPLAYER || UNITY_WINRT || UNITY_FLASH
  5.                                 documentsPath = UnityEngine.Application.persistentDataPath;
  6. #else
  7.                                 documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
  8. #endif
  9.         Debug.Log("My Documents path is: (" + documentsPath + ")");
  10.         Debug.Log("My Operating System is: (" + SystemInfo.operatingSystem + " / " + Application.platform.ToString() +")");
  11. }
  12.  

Pages: 1 ... 15 16 [17] 18 19