Skip to content

Commit

Permalink
[unity]添加websocket的支持(默认没有编译,需要的话加--websocket)
Browse files Browse the repository at this point in the history
  • Loading branch information
chexiongsheng committed Aug 22, 2024
1 parent 1425096 commit 223be87
Show file tree
Hide file tree
Showing 11 changed files with 710 additions and 6 deletions.
143 changes: 143 additions & 0 deletions unity/Assets/core/upm/Runtime/Resources/puerts/websocketpp.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Tencent is pleased to support the open source community by making Puerts available.
* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms.
* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package.
*/

var global = global || globalThis || (function () { return this; }());

const WebSocketPP = global.WebSocketPP;
//global.WebSocketPP = undefined;

class EventTarget {
constructor() {
this.listeners = {};
}

addEventListener(type, callback) {
if (!(type in this.listeners)) {
this.listeners[type] = [];
}
this.listeners[type].push(callback);
}

removeEventListener(type, callback) {
if (!(type in this.listeners)) {
return;
}
const stack = this.listeners[type];
for (let i = 0; i < stack.length; i++) {
if (stack[i] === callback) {
stack.splice(i, 1);
return;
}
}
}

dispatchEvent(ev) {
if (!(ev.type in this.listeners)) {
return true;
}
const stack = this.listeners[ev.type].slice();

for (let i = 0; i < stack.length; i++) {
stack[i].call(this, ev);
}
return !ev.defaultPrevented;
}
}

const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];

const poll_ws_objects = [];

class WebSocket extends EventTarget {
constructor(url, protocols) {
super();
if (protocols) throw new Error('do not support protocols argument');
this._raw = new WebSocketPP(url);
this._url = url;
// !!do not raise exception in handles.
this._raw.setHandles(
()=> {
this._readyState = WebSocket.OPEN;
this._addPendingEvent({type:'open'});
},
(data) => {
this._addPendingEvent({type:'message', data:data, origin:this._url});
},
(code, reason) => {
this._cleanup();
this._addPendingEvent({type:'close', code:code, reason: reason});
},
() => {
this._addPendingEvent({type:'error'});
this._cleanup();
this._addPendingEvent({type:'close', code:1006, reason: ""});
});

this._readyState = WebSocket.CONNECTING;
this._tid = setInterval(() => this._poll(), 1);
this._pendingEvents = [];
}

get url() {
return this._url;
}

get readyState() {
return this._readyState;
}

send(data) {
if (this._readyState !== WebSocket.OPEN) {
throw new Error(`WebSocket is not open: readyState ${this._readyState} (${readyStates[this._readyState]})`);
}
this._raw.send(data);
}

_cleanup() {
this._readyState = WebSocket.CLOSING;
}

_addPendingEvent(ev) {
this._pendingEvents.push(ev);
}

_poll() {
if (this._pendingEvents.length === 0 && this._readyState != WebSocket.CLOSING) {
this._raw.poll();
}
const ev = this._pendingEvents.shift();
if (ev) this.dispatchEvent(ev);
if (this._pendingEvents.length === 0 && this._readyState == WebSocket.CLOSING) {
this._raw = undefined;
clearInterval(this._tid);
this._readyState = WebSocket.CLOSED;
}
}

close(code, data) {
try {
this._raw.close(code, data);
} catch(e) {}
this._cleanup();
}

}

for (let i = 0; i < readyStates.length; i++) {
Object.defineProperty(WebSocket, readyStates[i], {
enumerable: true,
value: i
});

Object.defineProperty(WebSocket.prototype, readyStates[i], {
enumerable: true,
value: i
});
}

global.WebSocket = WebSocket;

2 changes: 2 additions & 0 deletions unity/Assets/core/upm/Runtime/Src/Default/JsEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ public JsEnv(ILoader loader, int debugPort, BackendType backend, IntPtr external
{
ExecuteModule("puerts/nodepatch.mjs");
}

ExecuteModule("puerts/websocketpp.mjs");

#if UNITY_EDITOR
if (OnJsEnvCreate != null)
Expand Down
1 change: 1 addition & 0 deletions unity/Assets/core/upm/Runtime/Src/IL2Cpp/JsEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public JsEnv(ILoader loader, int debugPort = -1)
ExecuteModule("puerts/events.mjs");
ExecuteModule("puerts/timer.mjs");
ExecuteModule("puerts/promises.mjs");
ExecuteModule("puerts/websocketpp.mjs");

this.debugPort = debugPort;
if (loader is IBuiltinLoadedListener)
Expand Down
1 change: 1 addition & 0 deletions unity/cli/cmd.mts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ program
.choices(["Release", "Debug"])
)
.option("--backend <backend>", "the JS backend will be used", "v8_9.4")
.option('-ws, --websocket', 'with websocket support')
.action(function (quickcommand, options) {
let backend = options.backend;
let config = options.config;
Expand Down
10 changes: 8 additions & 2 deletions unity/cli/make.mts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ interface BuildOptions {
config: 'Debug' | 'Release' | "RelWithDebInfo",
platform: 'osx' | 'win' | 'ios' | 'android' | 'linux' | 'ohos' | 'wasm',
arch: 'x64' | 'ia32' | 'armv7' | 'arm64' | 'wasm32' | 'auto',
backend: string
backend: string,
websocket?: boolean
}

//// 脚本 scripts
Expand Down Expand Up @@ -266,12 +267,17 @@ async function runPuertsMake(cwd: string, options: BuildOptions) {
mkdir('-p', CMAKE_BUILD_PATH);
mkdir('-p', OUTPUT_PATH)
const DArgsName = ['-DBACKEND_DEFINITIONS=', '-DBACKEND_LIB_NAMES=', '-DBACKEND_INC_NAMES=']
let CmakeDArgs = [definitionD, linkD, incD].map((r, index) => r ? DArgsName[index] + '"' + r + '"' : null).filter(t => t).join(' ');

if (options.websocket) {
CmakeDArgs += " -DWITH_WEBSOCKET=1";
}

var outputFile = BuildConfig.hook(
CMAKE_BUILD_PATH,
options,
cmakeAddedLibraryName,
[definitionD, linkD, incD].map((r, index) => r ? DArgsName[index] + '"' + r + '"' : null).filter(t => t).join(' ')
CmakeDArgs
);
if (isExecutable) return {};
if (!(outputFile instanceof Array)) outputFile = [outputFile];
Expand Down
9 changes: 6 additions & 3 deletions unity/cli/test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ export async function dotnetTest(cwd: string, backend: string, filter: string =
platform: process.platform == 'win32' ? 'win' : (process.platform == 'linux' ? 'linux' : 'osx'),
config: "Debug",
backend: backend || 'v8_9.4',
arch: process.arch as any
arch: process.arch as any,
websocket: true
})

// await runTest(cwd, copyConfig, true, filter);
Expand All @@ -163,7 +164,8 @@ export async function unityTest(cwd: string, unityPath: string) {
backend: 'nodejs_16',
platform: 'win',
config: 'Debug',
arch: 'x64'
arch: 'x64',
websocket: true
});

console.log("[Puer] Generating wrapper");
Expand All @@ -189,7 +191,8 @@ export async function unityTest(cwd: string, unityPath: string) {
backend: 'nodejs_16',
platform: 'win',
config: 'Debug',
arch: 'x64'
arch: 'x64',
websocket: true
});

console.log("[Puer] Building testplayer for v2");
Expand Down
8 changes: 7 additions & 1 deletion unity/native_src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ else ()
)
endif()

set(PUERTS_COMPILE_DEFINITIONS)

if ( WITH_WEBSOCKET )
list(APPEND PUERTS_COMPILE_DEFINITIONS WITH_WEBSOCKET)
list(APPEND PUERTS_SRC ${PROJECT_SOURCE_DIR}/../../unreal/Puerts/Source/JsEnv/Private/WebSocketImpl.cpp)
endif()

if(DEFINED PUERTS_EXTRA_SRC)
list(APPEND PUERTS_SRC ${PUERTS_EXTRA_SRC})
endif()
Expand Down Expand Up @@ -147,7 +154,6 @@ else ()
endif ()
endif ()

set(PUERTS_COMPILE_DEFINITIONS)
option ( USING_V8 "using v8" ON )
option ( USING_QJS "using quickjs" ON )
option ( USING_QJS_SUFFIX "add _qjs to namespace of simule v8 api " ON )
Expand Down
8 changes: 8 additions & 0 deletions unity/native_src/Src/BackendEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
#endif
#endif

#if defined(WITH_WEBSOCKET)
void InitWebsocketPPWrap(v8::Local<v8::Context> Context);
#endif

namespace PUERTS_NAMESPACE
{

Expand Down Expand Up @@ -344,6 +348,10 @@ void FBackendEnv::Initialize(void* external_quickjs_runtime, void* external_quic
Global->Set(Context, v8::String::NewFromUtf8(Isolate, EXECUTEMODULEGLOBANAME).ToLocalChecked(), v8::FunctionTemplate::New(Isolate, esmodule::ExecuteModule)->GetFunction(Context).ToLocalChecked()).Check();
Global->Set(Context, v8::String::NewFromUtf8(Isolate, "v8").ToLocalChecked(), GetV8Extras(Isolate, Context));
#endif

#if defined(WITH_WEBSOCKET)
InitWebsocketPPWrap(Context);
#endif
}

void FBackendEnv::UnInitialize()
Expand Down
8 changes: 8 additions & 0 deletions unity/native_src_il2cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ set ( PUERTS_SRC
${PROJECT_SOURCE_DIR}/../../unreal/Puerts/Source/JsEnv/Private/V8InspectorImpl.cpp
)

set(PUERTS_COMPILE_DEFINITIONS)

if ( WITH_WEBSOCKET )
list(APPEND PUERTS_COMPILE_DEFINITIONS WITH_WEBSOCKET)
list(APPEND PUERTS_SRC ${PROJECT_SOURCE_DIR}/../../unreal/Puerts/Source/JsEnv/Private/WebSocketImpl.cpp)
endif()

if(DEFINED PUERTS_EXTRA_SRC)
list(APPEND PUERTS_SRC ${PUERTS_EXTRA_SRC})
endif()
Expand Down Expand Up @@ -243,6 +250,7 @@ target_link_libraries(puerts_il2cpp
${BACKEND_LIB_NAMES}
)
target_compile_definitions (puerts_il2cpp PRIVATE ${BACKEND_DEFINITIONS})
target_compile_definitions (puerts_il2cpp PRIVATE ${PUERTS_COMPILE_DEFINITIONS})

if ( WIN32 AND NOT CYGWIN AND NOT ( CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" ) AND NOT ANDROID AND NOT MSYS)
set_property(TARGET puerts_il2cpp PROPERTY
Expand Down
100 changes: 100 additions & 0 deletions unity/test/Src/Cases/WebsocketTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

//#if PUERTS_GENERAL
namespace Puerts.UnitTest
{

[TestFixture]
public class WebsocketTest
{
static async Task RunEchoServer()
{
HttpListener httpListener = new HttpListener();
httpListener.Prefixes.Add("http://localhost:5000/");
httpListener.Start();
Console.WriteLine("Server started at http://localhost:5000/");

while (true)
{
HttpListenerContext httpContext = await httpListener.GetContextAsync();

if (httpContext.Request.IsWebSocketRequest)
{
HttpListenerWebSocketContext webSocketContext = await httpContext.AcceptWebSocketAsync(null);
WebSocket webSocket = webSocketContext.WebSocket;

await Echo(webSocket);
}
else
{
httpContext.Response.StatusCode = 400;
httpContext.Response.Close();
}
}
}

static async Task Echo(WebSocket webSocket)
{
byte[] buffer = new byte[1024];

while (webSocket.State == WebSocketState.Open)
{
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
else
{
string receivedMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine("Received: " + receivedMessage);

byte[] responseBuffer = Encoding.UTF8.GetBytes(receivedMessage);
await webSocket.SendAsync(new ArraySegment<byte>(responseBuffer), WebSocketMessageType.Text, true, CancellationToken.None);
Console.WriteLine("Echoed: " + receivedMessage);
}
}
}

[Test]
public void SmokeTest () {
RunEchoServer();
#if PUERTS_GENERAL
var jsEnv = new JsEnv(new TxtLoader());
#else
var jsEnv = new JsEnv(new UnitTestLoader());
#endif

jsEnv.Eval(@"
(function() {
let con = new WebSocket('ws://localhost:5000');
con.addEventListener('open', (ev) => {
console.log(`on open`);
con.send('puerts websocket');
});
con.addEventListener('message', (ev) => {
console.log(`on message: ${ev.data}`);
global.webSocketMessage = ev.data;
con.close();
});
})();
");
for(int i =0; i < 20; i++) {
jsEnv.Tick();
Thread.Sleep(10);
}

var res = jsEnv.Eval<string>("global.webSocketMessage");
Assert.AreEqual(res, "puerts websocket");
}
}
}
//#endif
Loading

0 comments on commit 223be87

Please sign in to comment.