Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET 4.x GS WebSocket Issue #4

Open
dylanh724 opened this issue Feb 5, 2020 · 17 comments
Open

.NET 4.x GS WebSocket Issue #4

dylanh724 opened this issue Feb 5, 2020 · 17 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@dylanh724
Copy link
Member

dylanh724 commented Feb 5, 2020

Ever get an extremely-vague { "error" : "timeout" } (or something like this)? Didn't get it before?
^ EDIT: This is deducted to be a 100% unrelated issue, now reposted here: #7

Well, it seems that this is caused by .NET 4.x issues -- specifically with WebSocket (dll). Although rare, this can cause serious damage to the player experience since it's almost impossible to handle all situations containing this. It's also hard to figure out where the timeout happened since absolutely no useful information is contained within this error.

Apparently, we can swap to a community version of WebSocket "with a few lines of code". However, I'm unfamiliar with this. Anyone wanna experiment with this?

Somewhat related

Fixing this bug would also likely resolve this issue: #3

Important Note

Native WebSocket does NOT include support for Windows 7 (Only Win8+). Any "Native" port will not work for us. We need a custom WebSocket implementation only.

Options

It is crucial that there is Windows 7 support included whatever we port to. We must use a non-native, open source edition. There are several 3 options:

  1. https://archive.codeplex.com/?p=websocket4net
  2. https://archive.codeplex.com/?p=superwebsocket
  3. https://github.com/sta/websocket-sharp // We chose this: See bottom comments to contribute.
@dylanh724 dylanh724 added bug Something isn't working help wanted Extra attention is needed labels Feb 5, 2020
@dylanh724 dylanh724 changed the title If using .NET 4.x on Windows standalone, sometimes calls max timeout .NET 4.x on Windows standalone { "error" : "timeout" } issue Feb 5, 2020
@dylanh724 dylanh724 changed the title .NET 4.x on Windows standalone { "error" : "timeout" } issue .NET 4.x Windows standalone { "error" : "timeout" } issue Feb 5, 2020
@dylanh724
Copy link
Member Author

@the one who should not be named (thx!) found the src of { error : timeout }, the vague C#4 err, here:
https://github.com/Imperium42/gs-csharp-community-sdk/blob/master/Projects/GameSparks.Api/Core/GSInstance.cs#L758

@dylanh724
Copy link
Member Author

dylanh724 commented Jun 17, 2020

As this ticket has been open for 4 months now, it doesn't look like it's going to happen. @gamesparks, if you are reading this, it would do the community a great favor for one more update to at least make things scalable ... we sort of need a headstart. This isn't just updating a small bug~ No one here is knowledgeable enough for a websocket replacement. We're pretty talented folks, but ... this is a bit beyond us.

Here are some alternatives (that support Windows 7, as native Microsoft WebSocket does not):

https://archive.codeplex.com/?p=websocket4net
https://archive.codeplex.com/?p=superwebsocket

@jconradi
Copy link

I changed out the websocket to be the same for all platforms - mostly because I didn't care about some like switch or xbox. I used this websocket as it works on all my target platforms: https://github.com/endel/NativeWebSocket

I no longer get timeouts on iOS after changing the websocket out.

@dylanh724
Copy link
Member Author

dylanh724 commented Jun 20, 2020

I no longer get timeouts on iOS after changing the websocket out.

Sounds promising! Do you mean you just swapped out a DLL, or required code? I have no idea where to even begin for this. Any chance of a pull request and/or mini guide, @jconradi ?

Also, does that lib support Win7, too? When I hear "native" and websocket together, sometimes I'm susp since Microsoft's native version doesn't support it.

@zarlin
Copy link

zarlin commented Jul 2, 2020

I stumbled upon this issue as well, and based on the NativeWebSocket @jconradi linked I made it work within my own project.
My implementation uses a custom websocket implementation to use instead of the SDK's GameSparksWebSocket. This means no changes need to be made to the SDK files. An alternative fix for this repository is to modify the GameSparksWebSocket to use NativeWebSocket.

My custom websocket implementation can be found here

To use it, modify DefaultPlatform in your project. In the GetSocket method, change

GameSparksWebSocket socket = new GameSparksWebSocket();

to

socket = new CustomGamesparksSocket();

Add CustomGamesparksSocket socket = null; as a class member variable, and add the following Update method to process received messages:

protected override void Update()
{
	if (socket != null && socket.State == GameSparksWebSocketState.Open)
		socket.DispatchMessageQueue();
	base.Update();
}

I've tested this solution on Android (Mono and IL2CPP) and iOS.

This may not be a perfect solution to include into the community SDK repository, but hopefully it can help others who are stuck with this problem.

@dylanh724
Copy link
Member Author

dylanh724 commented Jul 3, 2020

Nice, @zarlin ! I read that custom implementations should work with Win7, too (but then again, anything with the word "Native" in it combined with "Socket" often leaves out Win7 support; will need to test).

image

Seems like the CustomGamesparksSocket.cs doesn't know where NativeWebSocket is - anyone else experiencing this? I tried using GameSparks.Platforms.Native, but still no dice. Intellisense doesn't suggest using anything - strange.

I feel like I'm missing something right in front of me.

@dylanh724
Copy link
Member Author

https://github.com/sta/websocket-sharp feels promising if this doesn't work with Win7

@dylanh724
Copy link
Member Author

dylanh724 commented Jul 3, 2020

Thanks to your template, @zarlin , I almost got WebSocketSharp working, but 10 errors away: Could use some help ~

  1. Download the compiled WS# dll:
    image

  2. Download this gist, similar to @zarlin 's and throw it beside DefaultPlatform.cs:
    https://gist.github.com/dylanh724/4f0d2eb278c79bc1a9f5f8a9b4e353e0

  3. Similar instructions as above for DefaultPlatform edits from Zarlin:
    image

  4. Help with remaining errs :) Everything's in place. Just need some help finding replacements.
    image

@dylanh724
Copy link
Member Author

dylanh724 commented Jul 3, 2020

Update: Errs are gone (thx skoolbusdriver). Ignore above, just drop this:

image

Download (WIP 1)

Swapped Native WebSocket (lacking Win7 support) with websocket-sharp using @zarlin 's template (WIP):
GS-WebSocketSharp-WIP1.zip

Notes/Leads

  • CURRENT PROBLEM: GS.GameSparksAvailable won't fire -- nothing happens.
  • DispatchMessageQueue() was removed - seems integrated with the new socket lib, but not sure?
  • Open, Close, and Terminate were originally async => however, all funcs attached to it seem to not be async, so this was removed. Could be a misread on this?
  • If someone threw the entire unbuilt src in as native C#, you could probably step thru to see what's actually happening?

Edit

Unfortunately, that's about all the time I have for trial+error. We're REALLYYY close .... just need someone more knowledgeable on this topic to add the finishing touches (not sure why this isn't firing - probably some rookie mistake).

@dylanh724
Copy link
Member Author

dylanh724 commented Jul 4, 2020

Got it working!

Use this .zip below to swap out Native socket with WebSocketSharp with alleged win7 support.

Download

(Nov 28, 2020 EDIT: v3)
GS-WebSocketSharp-Nov28_2020-Final3.zip
No need for src code to use :) Just replaces/drops alongside DefaultPlatform.cs area -- structure should be intuitive.

TODO

  1. Needs testers: I'm about to test Win64 (Win10) standalone with IL2CPP. Good.
    • Works fine in Win10 x64 Unity editor Good.
    • Works fine on Win 10 x64 standalone (IL2CPP) Good.
  2. Need a specific tester for Win7 support: Someone please confirm this works on Win7.
  3. Initial Open() is never actually called by GS -- I just called it explicitly at init(): Someone should look into why Open() wasn't being called (could be important).
  4. If it works, need someone to swap this out in the src code, but not quite sure how these files are generated.
  5. Need someone to create a community fork of GS completely setup vanilla with community edition goodies. So instead of src code, it should be a Unity project working out-of-box. This src code is very NOT out of box :)

@dylanh724
Copy link
Member Author

dylanh724 commented Jul 8, 2020

So, this is it! Resolved. At least, this issue. We could open a new bug for fixing this from src instead of ghetto swap:

I'm not quite sure how src auto-gen's this stuff ... can anyone throw this in src? We could also use a "release" version, if someone wants to make an empty project with GS already thrown in.

@dylanh724 dylanh724 changed the title .NET 4.x Windows standalone { "error" : "timeout" } issue .NET 4.x GS WebSocket Issue Aug 25, 2020
@dylanh724 dylanh724 added the $$ Bounty $$ If you fix it, get rewarded with cash, gift cards, etc label Oct 12, 2020
@dylanh724
Copy link
Member Author

dylanh724 commented Oct 12, 2020

Offering Bounty

Offering 💸 $500 AWS coupon code to anyone that can figure out how to do all of the following:

  1. Apply my GS fix above in this GS C# community edition repo.
  2. Recompile GS community edition.
  3. Make a pull request for src code.
  4. Make a pull request for a 'release' with the recompiled files.

@dylanh724 dylanh724 reopened this Oct 12, 2020
@chorakm
Copy link

chorakm commented Nov 24, 2020

@dylanh724 I believe one thing that was missed was ping/pong requests. Refer to the GameSparksWebSocket.cs implementation. It enabled auto pinging at an interval of 30 seconds. In my testing, without this pinging the server sends a Close opcode after a period of inactivity which causes the client to close the connection on Stadia. WebSocketSharp has an IsAlive property you can check which sends a ping and waits for a pong. This is a blocking call however. In my own code I just created a Timer that calls the IsAlive getter every 30 seconds and this has prevented disconnects for us.

Edit:
It looks like there's a public Ping () method that would probably be more appropriate.

@dh-pt
Copy link

dh-pt commented Nov 24, 2020

Thanks @chorakm - after calling Ping(), how do you tell GS there's a heartbeat, assuming IsAlive may still be true? Or do I just "re" set IsAlive back to true and it will trigger things in the background? If false, do nothing (GS will handle d/c)? For the timer, do you use InvokeRepeating() or Update()?

Low priority, but any chance for a lazy-man's snippet if you happen to be staring at it? 👍

@chorakm
Copy link

chorakm commented Nov 24, 2020

Hey @dylanh-pt no problem. So to clarify, IsAlive is a computed property that calls Ping(). Ping returns true or false. It sends a Ping OpCode, and blocks to wait for a Pong opcode from the server. Because this is a blocking call, I did something similar to what's going on in WebSocket4Net where they create a Timer to send a ping on an interval in a thread pool. GSConnection doesn't seem to care whats going on underneath, just that the socket is open and ready. Sending the ping every 30 seconds seemed to prevent the GameSparks backend from sending the Close opcode, which I assume they do because they can't tell if the client on the other side of the TCP connection is still there, so they just terminate the connection.

I'm uncertain as to why this is only occurring on Stadia however. May be something specific to Linux

Here is what I did in CustomGameSparksSocket.cs:

private void initAllExceptMsg(string url, Action _onClose, Action _onOpen, Action<string> _onError)
{
    socket = new WebSocket(url);
    socket.OnOpen += (sender, e) => { OnOpen(); _onOpen(); };
    socket.OnClose += (sender, e) => { OnClose(); _onClose(); };
    socket.OnError += (sender, e) => OnSocketError(e.Message, _onError);
}

private void OnOpen()
{
    this.webSocketTimer = new Timer(OnPingTimerCallback, null, 30 * 1000, 30 * 1000);
}

private void OnClose()
{
    this.webSocketTimer.Change(Timeout.Infinite, Timeout.Infinite);
    this.webSocketTimer.Dispose();
}

private void OnPingTimerCallback(object state)
{
    socket.Ping();
}

@dh-pt
Copy link

dh-pt commented Nov 25, 2020

Thanks for that! When I get to my own time, I'll toss up a new zip and relook over that pull request from the other guy. With his pull request, we can see where to edit and possibly toss this in, too.

@dylanh724
Copy link
Member Author

GS-WebSocketSharp-Nov28_2020-Final3.zip
Here is the updated patch (v3) - I'll edit above, too.

@dylanh724 dylanh724 removed the $$ Bounty $$ If you fix it, get rewarded with cash, gift cards, etc label Nov 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants