Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
W
windbot
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
windbot
Commits
740de21f
Commit
740de21f
authored
Feb 16, 2026
by
nanahira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
reverse ws
parent
ecbc3fc0
Pipeline
#43245
failed with stages
in 76 minutes and 33 seconds
Changes
14
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1433 additions
and
422 deletions
+1433
-422
BotWrapper/BotWrapper.csproj
BotWrapper/BotWrapper.csproj
+62
-62
Dialogs/default.json
Dialogs/default.json
+0
-0
Game/GameClient.cs
Game/GameClient.cs
+189
-56
Program.cs
Program.cs
+24
-54
ServerModeHost.cs
ServerModeHost.cs
+260
-0
WindBot.csproj
WindBot.csproj
+230
-225
WindBotInfo.cs
WindBotInfo.cs
+2
-0
YGOSharp.Network/BinaryClient.cs
YGOSharp.Network/BinaryClient.cs
+13
-12
YGOSharp.Network/INetworkClient.cs
YGOSharp.Network/INetworkClient.cs
+22
-0
YGOSharp.Network/NetworkClient.cs
YGOSharp.Network/NetworkClient.cs
+4
-4
YGOSharp.Network/ReverseWsClient.cs
YGOSharp.Network/ReverseWsClient.cs
+149
-0
YGOSharp.Network/ReverseWsSocketClient.cs
YGOSharp.Network/ReverseWsSocketClient.cs
+293
-0
YGOSharp.Network/WsClient.cs
YGOSharp.Network/WsClient.cs
+176
-0
YGOSharp.Network/YGOClient.cs
YGOSharp.Network/YGOClient.cs
+9
-9
No files found.
BotWrapper/BotWrapper.csproj
View file @
740de21f
<?xml version="1.0" encoding="utf-8"?>
<Project
ToolsVersion=
"14.0"
DefaultTargets=
"Build"
xmlns=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<Import
Project=
"$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition=
"Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"
/>
<PropertyGroup>
<Configuration
Condition=
" '$(Configuration)' == '' "
>
Debug
</Configuration>
<Platform
Condition=
" '$(Platform)' == '' "
>
AnyCPU
</Platform>
<ProjectGuid>
{0665CA3B-C14F-40EC-ABFB-AD46A695F5A3}
</ProjectGuid>
<OutputType>
WinExe
</OutputType>
<AppDesignerFolder>
Properties
</AppDesignerFolder>
<RootNamespace>
BotWrapper
</RootNamespace>
<AssemblyName>
Bot
</AssemblyName>
<TargetFrameworkVersion>
v4.8
</TargetFrameworkVersion>
<FileAlignment>
512
</FileAlignment>
<TargetFrameworkProfile
/>
</PropertyGroup>
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Debug|x86'"
>
<PlatformTarget>
x86
</PlatformTarget>
<DebugSymbols>
true
</DebugSymbols>
<DebugType>
full
</DebugType>
<Optimize>
false
</Optimize>
<OutputPath>
bin\Debug\
</OutputPath>
<DefineConstants>
DEBUG;TRACE
</DefineConstants>
<ErrorReport>
prompt
</ErrorReport>
<WarningLevel>
4
</WarningLevel>
<Prefer32Bit>
false
</Prefer32Bit>
</PropertyGroup>
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Release|x86'"
>
<PlatformTarget>
x86
</PlatformTarget>
<DebugType>
pdbonly
</DebugType>
<Optimize>
true
</Optimize>
<OutputPath>
bin\Release\
</OutputPath>
<DefineConstants>
TRACE
</DefineConstants>
<ErrorReport>
prompt
</ErrorReport>
<WarningLevel>
4
</WarningLevel>
<Prefer32Bit>
false
</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject>
BotWrapper.BotWrapper
</StartupObject>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>
WindBot.ico
</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference
Include=
"System"
/>
<Reference
Include=
"System.Data"
/>
<Reference
Include=
"System.Xml"
/>
</ItemGroup>
<ItemGroup>
<Compile
Include=
"BotWrapper.cs"
/>
<Compile
Include=
"Properties\AssemblyInfo.cs"
/>
</ItemGroup>
<ItemGroup>
<None
Include=
"app.config"
/>
<None
Include=
"bot.conf"
>
<CopyToOutputDirectory>
PreserveNewest
</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content
Include=
"WindBot.ico"
/>
</ItemGroup>
<Import
Project=
"$(MSBuildToolsPath)\Microsoft.CSharp.targets"
/>
<?xml version="1.0" encoding="utf-8"?>
<Project
ToolsVersion=
"14.0"
DefaultTargets=
"Build"
xmlns=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<Import
Project=
"$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition=
"Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"
/>
<PropertyGroup>
<Configuration
Condition=
" '$(Configuration)' == '' "
>
Debug
</Configuration>
<Platform
Condition=
" '$(Platform)' == '' "
>
AnyCPU
</Platform>
<ProjectGuid>
{0665CA3B-C14F-40EC-ABFB-AD46A695F5A3}
</ProjectGuid>
<OutputType>
WinExe
</OutputType>
<AppDesignerFolder>
Properties
</AppDesignerFolder>
<RootNamespace>
BotWrapper
</RootNamespace>
<AssemblyName>
Bot
</AssemblyName>
<TargetFrameworkVersion>
v4.8
</TargetFrameworkVersion>
<FileAlignment>
512
</FileAlignment>
<TargetFrameworkProfile
/>
</PropertyGroup>
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Debug|x86'"
>
<PlatformTarget>
x86
</PlatformTarget>
<DebugSymbols>
true
</DebugSymbols>
<DebugType>
full
</DebugType>
<Optimize>
false
</Optimize>
<OutputPath>
bin\Debug\
</OutputPath>
<DefineConstants>
DEBUG;TRACE
</DefineConstants>
<ErrorReport>
prompt
</ErrorReport>
<WarningLevel>
4
</WarningLevel>
<Prefer32Bit>
false
</Prefer32Bit>
</PropertyGroup>
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Release|x86'"
>
<PlatformTarget>
x86
</PlatformTarget>
<DebugType>
pdbonly
</DebugType>
<Optimize>
true
</Optimize>
<OutputPath>
bin\Release\
</OutputPath>
<DefineConstants>
TRACE
</DefineConstants>
<ErrorReport>
prompt
</ErrorReport>
<WarningLevel>
4
</WarningLevel>
<Prefer32Bit>
false
</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject>
BotWrapper.BotWrapper
</StartupObject>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>
WindBot.ico
</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference
Include=
"System"
/>
<Reference
Include=
"System.Data"
/>
<Reference
Include=
"System.Xml"
/>
</ItemGroup>
<ItemGroup>
<Compile
Include=
"BotWrapper.cs"
/>
<Compile
Include=
"Properties\AssemblyInfo.cs"
/>
</ItemGroup>
<ItemGroup>
<None
Include=
"app.config"
/>
<None
Include=
"bot.conf"
>
<CopyToOutputDirectory>
PreserveNewest
</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content
Include=
"WindBot.ico"
/>
</ItemGroup>
<Import
Project=
"$(MSBuildToolsPath)\Microsoft.CSharp.targets"
/>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
-->
</Project>
\ No newline at end of file
Dialogs/
PAST/
default.json
→
Dialogs/default.json
View file @
740de21f
File moved
Game/GameClient.cs
View file @
740de21f
using
System
;
using
System.IO
;
using
System.Linq
;
using
System.Net
;
using
YGOSharp.Network
;
using
YGOSharp.Network.Enums
;
using
YGOSharp.Network.Utils
;
using
System
;
using
System.IO
;
using
System.Linq
;
using
System.Net
;
using
System.Net.Sockets
;
using
YGOSharp.Network
;
using
YGOSharp.Network.Enums
;
using
YGOSharp.Network.Utils
;
namespace
WindBot.Game
{
...
...
@@ -17,60 +18,192 @@ namespace WindBot.Game
public
string
DeckCode
;
public
string
Dialog
;
public
int
Hand
;
public
bool
Debug
;
public
bool
_chat
;
private
string
_serverHost
;
private
int
_serverPort
;
private
short
_proVersion
;
private
string
_roomInfo
;
private
GameBehavior
_behavior
;
public
GameClient
(
WindBotInfo
Info
)
{
Username
=
Info
.
Name
;
Deck
=
Info
.
Deck
;
public
bool
Debug
;
public
bool
_chat
;
private
string
_serverHost
;
private
int
_serverPort
;
private
string
_realIP
;
private
short
_proVersion
;
private
string
_roomInfo
;
private
readonly
YGOClient
_presetConnection
;
private
GameBehavior
_behavior
;
private
enum
ConnectionMode
{
Tcp
,
WebSocket
}
private
class
ConnectionTarget
{
public
ConnectionMode
Mode
;
public
string
ExternalHost
;
public
string
TcpHost
;
public
int
TcpPort
;
public
Uri
WebSocketUri
;
}
public
GameClient
(
WindBotInfo
Info
,
YGOClient
presetConnection
=
null
)
{
Username
=
Info
.
Name
;
Deck
=
Info
.
Deck
;
DeckFile
=
Info
.
DeckFile
;
DeckCode
=
Info
.
DeckCode
;
Dialog
=
Info
.
Dialog
;
Hand
=
Info
.
Hand
;
Debug
=
Info
.
Debug
;
_chat
=
Info
.
Chat
;
_serverHost
=
Info
.
Host
;
_serverPort
=
Info
.
Port
;
_roomInfo
=
Info
.
HostInfo
;
_proVersion
=
(
short
)
Info
.
Version
;
}
public
void
Start
()
{
Connection
=
new
YGOClient
();
_behavior
=
new
GameBehavior
(
this
);
Connection
.
Connected
+=
OnConnected
;
Connection
.
PacketReceived
+=
OnPacketReceived
;
IPAddress
target_address
;
try
{
target_address
=
IPAddress
.
Parse
(
_serverHost
);
}
catch
(
System
.
Exception
)
{
IPHostEntry
_hostEntry
=
Dns
.
GetHostEntry
(
_serverHost
);
target_address
=
_hostEntry
.
AddressList
.
FirstOrDefault
(
findIPv4
=>
findIPv4
.
AddressFamily
==
System
.
Net
.
Sockets
.
AddressFamily
.
InterNetwork
);
}
Connection
.
Connect
(
target_address
,
_serverPort
);
}
private
void
OnConnected
()
{
BinaryWriter
packet
=
GamePacketFactory
.
Create
(
CtosMessage
.
ExternalAddress
);
packet
.
Write
((
UInt32
)
0
);
// real_ip, is always 0 in normal client
packet
.
WriteUnicodeAutoLength
(
_serverHost
,
255
);
Connection
.
Send
(
packet
);
_chat
=
Info
.
Chat
;
_serverHost
=
Info
.
Host
;
_serverPort
=
Info
.
Port
;
_realIP
=
Info
.
RealIP
;
_roomInfo
=
Info
.
HostInfo
;
_proVersion
=
(
short
)
Info
.
Version
;
_presetConnection
=
presetConnection
;
}
public
void
Start
()
{
bool
shouldStartWsConnect
=
false
;
WsClient
wsClient
=
null
;
IPAddress
tcpAddress
=
null
;
if
(
_presetConnection
!=
null
)
{
Connection
=
_presetConnection
;
}
else
{
ConnectionTarget
target
=
ResolveConnectionTarget
();
_serverHost
=
target
.
ExternalHost
;
if
(
target
.
Mode
==
ConnectionMode
.
WebSocket
)
{
wsClient
=
new
WsClient
(
target
.
WebSocketUri
);
Connection
=
new
YGOClient
(
wsClient
);
shouldStartWsConnect
=
true
;
}
else
{
_serverPort
=
target
.
TcpPort
;
Connection
=
new
YGOClient
();
try
{
tcpAddress
=
IPAddress
.
Parse
(
target
.
TcpHost
);
}
catch
(
System
.
Exception
)
{
IPHostEntry
hostEntry
=
Dns
.
GetHostEntry
(
target
.
TcpHost
);
tcpAddress
=
hostEntry
.
AddressList
.
FirstOrDefault
(
findIPv4
=>
findIPv4
.
AddressFamily
==
AddressFamily
.
InterNetwork
);
if
(
tcpAddress
==
null
)
throw
new
Exception
(
"Can't resolve an IPv4 address for host '"
+
target
.
TcpHost
+
"'."
);
}
}
}
_behavior
=
new
GameBehavior
(
this
);
RegisterConnectionEvents
();
if
(
shouldStartWsConnect
)
{
wsClient
.
StartConnect
();
return
;
}
if
(
_presetConnection
==
null
)
Connection
.
Connect
(
tcpAddress
,
_serverPort
);
}
private
void
RegisterConnectionEvents
()
{
Connection
.
Connected
+=
OnConnected
;
Connection
.
PacketReceived
+=
OnPacketReceived
;
}
private
ConnectionTarget
ResolveConnectionTarget
()
{
if
(
string
.
IsNullOrEmpty
(
_serverHost
))
throw
new
Exception
(
"Host is empty."
);
if
(
_serverHost
.
IndexOf
(
"//"
,
StringComparison
.
Ordinal
)
<
0
)
{
return
new
ConnectionTarget
{
Mode
=
ConnectionMode
.
Tcp
,
ExternalHost
=
_serverHost
,
TcpHost
=
_serverHost
,
TcpPort
=
_serverPort
};
}
Uri
uri
;
if
(!
Uri
.
TryCreate
(
_serverHost
,
UriKind
.
Absolute
,
out
uri
))
throw
new
Exception
(
"Invalid Host URL: '"
+
_serverHost
+
"'."
);
string
scheme
=
uri
.
Scheme
.
ToLowerInvariant
();
if
(
scheme
==
"tcp"
)
{
if
(
string
.
IsNullOrEmpty
(
uri
.
Host
))
throw
new
Exception
(
"Invalid TCP Host URL: '"
+
_serverHost
+
"'."
);
int
resolvedPort
=
_serverPort
;
if
(!
uri
.
IsDefaultPort
)
resolvedPort
=
uri
.
Port
;
return
new
ConnectionTarget
{
Mode
=
ConnectionMode
.
Tcp
,
ExternalHost
=
uri
.
Host
,
TcpHost
=
uri
.
Host
,
TcpPort
=
resolvedPort
};
}
if
(
scheme
==
"ws"
||
scheme
==
"wss"
)
{
return
new
ConnectionTarget
{
Mode
=
ConnectionMode
.
WebSocket
,
ExternalHost
=
uri
.
Host
,
WebSocketUri
=
uri
};
}
throw
new
Exception
(
"Unsupported Host scheme '"
+
uri
.
Scheme
+
"'. Supported schemes: tcp, ws, wss."
);
}
private
uint
ParseRealIpAsNetworkOrderInt32
()
{
if
(
string
.
IsNullOrWhiteSpace
(
_realIP
))
return
0
;
string
text
=
_realIP
.
Trim
();
if
(
text
.
StartsWith
(
"::ffff:"
,
StringComparison
.
OrdinalIgnoreCase
))
text
=
text
.
Substring
(
"::ffff:"
.
Length
);
IPAddress
parsedIp
;
if
(!
IPAddress
.
TryParse
(
text
,
out
parsedIp
))
throw
new
Exception
(
"Invalid RealIP: '"
+
_realIP
+
"'."
);
if
(
parsedIp
.
AddressFamily
==
AddressFamily
.
InterNetworkV6
&&
parsedIp
.
IsIPv4MappedToIPv6
)
parsedIp
=
parsedIp
.
MapToIPv4
();
if
(
parsedIp
.
AddressFamily
!=
AddressFamily
.
InterNetwork
)
throw
new
Exception
(
"RealIP must be an IPv4 address: '"
+
_realIP
+
"'."
);
byte
[]
bytes
=
parsedIp
.
GetAddressBytes
();
uint
networkOrdered
=
((
uint
)
bytes
[
0
]
<<
24
)
|
((
uint
)
bytes
[
1
]
<<
16
)
|
((
uint
)
bytes
[
2
]
<<
8
)
|
bytes
[
3
];
return
unchecked
((
uint
)
IPAddress
.
NetworkToHostOrder
((
int
)
networkOrdered
));
}
private
void
OnConnected
()
{
BinaryWriter
packet
=
GamePacketFactory
.
Create
(
CtosMessage
.
ExternalAddress
);
packet
.
Write
(
ParseRealIpAsNetworkOrderInt32
());
packet
.
WriteUnicodeAutoLength
(
_serverHost
,
255
);
Connection
.
Send
(
packet
);
packet
=
GamePacketFactory
.
Create
(
CtosMessage
.
PlayerInfo
);
packet
.
WriteUnicode
(
Username
,
20
);
...
...
Program.cs
View file @
740de21f
...
...
@@ -5,6 +5,7 @@ using System.Net;
using
System.Web
;
using
WindBot.Game
;
using
WindBot.Game.AI
;
using
YGOSharp.Network
;
using
YGOSharp.OCGWrapper
;
namespace
WindBot
...
...
@@ -72,7 +73,6 @@ namespace WindBot
Info
.
Name
=
Config
.
GetString
(
"Name"
,
Info
.
Name
);
Info
.
Deck
=
Config
.
GetString
(
"Deck"
,
Info
.
Deck
);
Info
.
DeckFile
=
Config
.
GetString
(
"DeckFile"
,
Info
.
DeckFile
);
Info
.
DeckCode
=
Config
.
GetString
(
"DeckCode"
,
Info
.
DeckCode
);
Info
.
Dialog
=
Config
.
GetString
(
"Dialog"
,
Info
.
Dialog
);
Info
.
Host
=
Config
.
GetString
(
"Host"
,
Info
.
Host
);
Info
.
Port
=
Config
.
GetInt
(
"Port"
,
Info
.
Port
);
...
...
@@ -81,40 +81,28 @@ namespace WindBot
Info
.
Hand
=
Config
.
GetInt
(
"Hand"
,
Info
.
Hand
);
Info
.
Debug
=
Config
.
GetBool
(
"Debug"
,
Info
.
Debug
);
Info
.
Chat
=
Config
.
GetBool
(
"Chat"
,
Info
.
Chat
);
Info
.
DeckCode
=
Config
.
GetString
(
"DeckCode"
,
Info
.
DeckCode
);
Info
.
RealIP
=
Config
.
GetString
(
"RealIP"
,
Info
.
RealIP
);
Run
(
Info
);
}
private
static
void
RunAsServer
(
int
ServerPort
)
{
using
(
HttpListener
MainServer
=
new
HttpListener
())
{
MainServer
.
AuthenticationSchemes
=
AuthenticationSchemes
.
Anonymous
;
MainServer
.
Prefixes
.
Add
(
"http://+:"
+
ServerPort
+
"/"
);
MainServer
.
Start
();
Logger
.
WriteLine
(
"WindBot server start successed."
);
Logger
.
WriteLine
(
"HTTP GET http://127.0.0.1:"
+
ServerPort
+
"/?name=WindBot&host=127.0.0.1&port=7911 to call the bot."
);
while
(
true
)
ServerModeHost
.
Run
(
ServerPort
,
new
ParameterizedThreadStart
(
Run
),
delegate
(
string
rawUrl
,
out
WindBotInfo
Info
,
out
string
port
)
{
#if !DEBUG
try
{
#endif
HttpListenerContext
ctx
=
MainServer
.
GetContext
();
WindBotInfo
Info
=
new
WindBotInfo
();
string
RawUrl
=
Path
.
GetFileName
(
ctx
.
Request
.
RawUrl
);
Info
=
new
WindBotInfo
();
string
RawUrl
=
Path
.
GetFileName
(
rawUrl
);
Info
.
Name
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"name"
);
Info
.
Deck
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"deck"
);
Info
.
Host
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"host"
);
string
port
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"port"
);
port
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"port"
);
if
(
port
!=
null
)
Info
.
Port
=
Int32
.
Parse
(
port
);
string
deckfile
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"deckfile"
);
if
(
deckfile
!=
null
)
Info
.
DeckFile
=
deckfile
;
string
deckcode
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"deckcode"
);
if
(
deckcode
!=
null
)
Info
.
DeckCode
=
deckcode
;
string
dialog
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"dialog"
);
if
(
dialog
!=
null
)
Info
.
Dialog
=
dialog
;
...
...
@@ -133,39 +121,13 @@ namespace WindBot
string
chat
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"chat"
);
if
(
chat
!=
null
)
Info
.
Chat
=
bool
.
Parse
(
chat
);
if
(
Info
.
Name
==
null
||
Info
.
Host
==
null
||
port
==
null
)
{
ctx
.
Response
.
StatusCode
=
400
;
ctx
.
Response
.
Close
();
}
else
{
#if !DEBUG
try
{
#endif
Thread
workThread
=
new
Thread
(
new
ParameterizedThreadStart
(
Run
));
workThread
.
Start
(
Info
);
#if !DEBUG
}
catch
(
Exception
ex
)
{
Logger
.
WriteErrorLine
(
"Start Thread Error: "
+
ex
);
}
#endif
ctx
.
Response
.
StatusCode
=
200
;
ctx
.
Response
.
Close
();
}
#if !DEBUG
}
catch
(
Exception
ex
)
{
Logger
.
WriteErrorLine
(
"Parse Http Request Error: "
+
ex
);
}
#endif
}
}
string
deckcode
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"deckcode"
);
if
(
deckcode
!=
null
)
Info
.
DeckCode
=
deckcode
;
string
realIP
=
HttpUtility
.
ParseQueryString
(
RawUrl
).
Get
(
"realip"
);
if
(
realIP
!=
null
)
Info
.
RealIP
=
realIP
;
});
}
private
static
void
Run
(
object
o
)
...
...
@@ -175,8 +137,16 @@ namespace WindBot
{
//all errors will be catched instead of causing the program to crash.
#endif
WindBotInfo
Info
=
(
WindBotInfo
)
o
;
GameClient
client
=
new
GameClient
(
Info
);
WindBotInfo
Info
=
o
as
WindBotInfo
;
YGOClient
presetConnection
=
null
;
ServerRunRequest
request
=
o
as
ServerRunRequest
;
if
(
request
!=
null
)
{
Info
=
request
.
Info
;
presetConnection
=
request
.
Connection
;
}
GameClient
client
=
(
presetConnection
==
null
)
?
new
GameClient
(
Info
)
:
new
GameClient
(
Info
,
presetConnection
);
client
.
Start
();
Logger
.
DebugWriteLine
(
client
.
Username
+
" started."
);
while
(
client
.
Connection
.
IsConnected
)
...
...
ServerModeHost.cs
0 → 100644
View file @
740de21f
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.Net
;
using
System.Net.Sockets
;
using
System.Security.Cryptography
;
using
System.Text
;
using
System.Threading
;
using
YGOSharp.Network
;
namespace
WindBot
{
internal
class
ServerRunRequest
{
public
WindBotInfo
Info
;
public
YGOClient
Connection
;
}
internal
delegate
void
ParseServerInfoCallback
(
string
rawUrl
,
out
WindBotInfo
info
,
out
string
port
);
internal
static
class
ServerModeHost
{
internal
static
void
Run
(
int
serverPort
,
ParameterizedThreadStart
runCallback
,
ParseServerInfoCallback
parseInfoCallback
)
{
TcpListener
mainServer
=
new
TcpListener
(
IPAddress
.
Any
,
serverPort
);
mainServer
.
Start
();
while
(
true
)
{
#if !DEBUG
try
{
#endif
Socket
socket
=
mainServer
.
AcceptSocket
();
ThreadPool
.
QueueUserWorkItem
(
delegate
{
HandleServerSocket
(
socket
,
runCallback
,
parseInfoCallback
);
});
#if !DEBUG
}
catch
(
Exception
ex
)
{
Logger
.
WriteErrorLine
(
"Accept Socket Error: "
+
ex
);
}
#endif
}
}
private
static
void
HandleServerSocket
(
Socket
socket
,
ParameterizedThreadStart
runCallback
,
ParseServerInfoCallback
parseInfoCallback
)
{
bool
handoffToBot
=
false
;
try
{
string
method
;
string
rawUrl
;
Dictionary
<
string
,
string
>
headers
;
if
(!
TryReadHttpRequest
(
socket
,
out
method
,
out
rawUrl
,
out
headers
))
return
;
WindBotInfo
info
;
string
port
;
parseInfoCallback
(
rawUrl
,
out
info
,
out
port
);
if
(
info
==
null
)
info
=
new
WindBotInfo
();
string
upgradeHeader
=
GetHeader
(
headers
,
"upgrade"
);
string
connectionHeader
=
GetHeader
(
headers
,
"connection"
);
string
webSocketKey
=
GetHeader
(
headers
,
"sec-websocket-key"
);
string
webSocketVersion
=
GetHeader
(
headers
,
"sec-websocket-version"
);
bool
isWebSocketRequest
=
string
.
Equals
(
method
,
"GET"
,
StringComparison
.
OrdinalIgnoreCase
)
&&
!
string
.
IsNullOrEmpty
(
webSocketKey
)
&&
!
string
.
IsNullOrEmpty
(
upgradeHeader
)
&&
upgradeHeader
.
Equals
(
"websocket"
,
StringComparison
.
OrdinalIgnoreCase
)
&&
!
string
.
IsNullOrEmpty
(
connectionHeader
)
&&
connectionHeader
.
IndexOf
(
"upgrade"
,
StringComparison
.
OrdinalIgnoreCase
)
>=
0
;
if
(
isWebSocketRequest
)
{
if
(
info
.
Name
==
null
)
info
.
Name
=
"WindBot"
;
if
(
info
.
Host
==
null
)
{
IPEndPoint
remote
=
socket
.
RemoteEndPoint
as
IPEndPoint
;
info
.
Host
=
remote
!=
null
?
remote
.
Address
.
ToString
()
:
"reverse-ws"
;
}
if
(!
string
.
IsNullOrEmpty
(
webSocketVersion
)
&&
webSocketVersion
!=
"13"
)
{
WriteHttpResponse
(
socket
,
426
,
"Upgrade Required"
,
"Unsupported WebSocket Version"
);
return
;
}
string
acceptKey
=
ComputeWebSocketAccept
(
webSocketKey
);
StringBuilder
response
=
new
StringBuilder
();
response
.
Append
(
"HTTP/1.1 101 Switching Protocols\r\n"
);
response
.
Append
(
"Upgrade: websocket\r\n"
);
response
.
Append
(
"Connection: Upgrade\r\n"
);
response
.
Append
(
"Sec-WebSocket-Accept: "
).
Append
(
acceptKey
).
Append
(
"\r\n\r\n"
);
byte
[]
responseBytes
=
Encoding
.
ASCII
.
GetBytes
(
response
.
ToString
());
socket
.
Send
(
responseBytes
);
ReverseWsSocketClient
reverseWsClient
=
new
ReverseWsSocketClient
(
socket
);
YGOClient
ygoClient
=
new
YGOClient
(
reverseWsClient
);
ServerRunRequest
request
=
new
ServerRunRequest
();
request
.
Info
=
info
;
request
.
Connection
=
ygoClient
;
Thread
workThread
=
new
Thread
(
runCallback
);
workThread
.
Start
(
request
);
handoffToBot
=
true
;
return
;
}
bool
canUseHostWithoutPort
=
false
;
if
(
info
.
Host
!=
null
&&
info
.
Host
.
IndexOf
(
"//"
,
StringComparison
.
Ordinal
)
>=
0
)
{
Uri
uri
;
if
(
Uri
.
TryCreate
(
info
.
Host
,
UriKind
.
Absolute
,
out
uri
))
{
string
scheme
=
uri
.
Scheme
.
ToLowerInvariant
();
canUseHostWithoutPort
=
(
scheme
==
"ws"
||
scheme
==
"wss"
)
||
(
scheme
==
"tcp"
&&
!
uri
.
IsDefaultPort
);
}
}
if
(
info
.
Name
==
null
||
info
.
Host
==
null
||
(
port
==
null
&&
!
canUseHostWithoutPort
))
{
WriteHttpResponse
(
socket
,
400
,
"Bad Request"
,
""
);
return
;
}
#if !DEBUG
try
{
#endif
Thread
normalWorkThread
=
new
Thread
(
runCallback
);
normalWorkThread
.
Start
(
info
);
#if !DEBUG
}
catch
(
Exception
ex
)
{
Logger
.
WriteErrorLine
(
"Start Thread Error: "
+
ex
);
}
#endif
WriteHttpResponse
(
socket
,
200
,
"OK"
,
""
);
#if !DEBUG
}
catch
(
Exception
ex
)
{
Logger
.
WriteErrorLine
(
"Handle Socket Request Error: "
+
ex
);
}
#endif
finally
{
if
(!
handoffToBot
)
{
try
{
socket
.
Shutdown
(
SocketShutdown
.
Both
);
}
catch
{
}
socket
.
Close
();
}
}
}
private
static
bool
TryReadHttpRequest
(
Socket
socket
,
out
string
method
,
out
string
rawUrl
,
out
Dictionary
<
string
,
string
>
headers
)
{
method
=
null
;
rawUrl
=
null
;
headers
=
new
Dictionary
<
string
,
string
>(
StringComparer
.
OrdinalIgnoreCase
);
NetworkStream
stream
=
new
NetworkStream
(
socket
,
false
);
MemoryStream
headerBuffer
=
new
MemoryStream
();
int
state
=
0
;
while
(
headerBuffer
.
Length
<
65536
)
{
int
b
=
stream
.
ReadByte
();
if
(
b
<
0
)
break
;
headerBuffer
.
WriteByte
((
byte
)
b
);
if
(
state
==
0
)
state
=
(
b
==
'\r'
)
?
1
:
0
;
else
if
(
state
==
1
)
state
=
(
b
==
'\n'
)
?
2
:
0
;
else
if
(
state
==
2
)
state
=
(
b
==
'\r'
)
?
3
:
0
;
else
if
(
state
==
3
)
{
if
(
b
==
'\n'
)
break
;
state
=
0
;
}
}
string
requestText
=
Encoding
.
ASCII
.
GetString
(
headerBuffer
.
ToArray
());
if
(
string
.
IsNullOrEmpty
(
requestText
))
return
false
;
string
[]
lines
=
requestText
.
Split
(
new
[]
{
"\r\n"
},
StringSplitOptions
.
None
);
if
(
lines
.
Length
==
0
||
string
.
IsNullOrWhiteSpace
(
lines
[
0
]))
return
false
;
string
[]
requestLineParts
=
lines
[
0
].
Split
(
' '
);
if
(
requestLineParts
.
Length
<
3
)
return
false
;
method
=
requestLineParts
[
0
];
rawUrl
=
requestLineParts
[
1
];
for
(
int
i
=
1
;
i
<
lines
.
Length
;
++
i
)
{
string
line
=
lines
[
i
];
if
(
string
.
IsNullOrEmpty
(
line
))
break
;
int
colon
=
line
.
IndexOf
(
':'
);
if
(
colon
<=
0
)
continue
;
string
key
=
line
.
Substring
(
0
,
colon
).
Trim
();
string
value
=
line
.
Substring
(
colon
+
1
).
Trim
();
headers
[
key
]
=
value
;
}
return
true
;
}
private
static
string
GetHeader
(
Dictionary
<
string
,
string
>
headers
,
string
key
)
{
string
value
;
if
(
headers
.
TryGetValue
(
key
,
out
value
))
return
value
;
return
null
;
}
private
static
void
WriteHttpResponse
(
Socket
socket
,
int
statusCode
,
string
statusText
,
string
body
)
{
if
(
body
==
null
)
body
=
""
;
byte
[]
bodyBytes
=
Encoding
.
UTF8
.
GetBytes
(
body
);
StringBuilder
builder
=
new
StringBuilder
();
builder
.
Append
(
"HTTP/1.1 "
).
Append
(
statusCode
).
Append
(
' '
).
Append
(
statusText
).
Append
(
"\r\n"
);
builder
.
Append
(
"Connection: close\r\n"
);
builder
.
Append
(
"Content-Type: text/plain; charset=utf-8\r\n"
);
builder
.
Append
(
"Content-Length: "
).
Append
(
bodyBytes
.
Length
).
Append
(
"\r\n\r\n"
);
byte
[]
headerBytes
=
Encoding
.
ASCII
.
GetBytes
(
builder
.
ToString
());
socket
.
Send
(
headerBytes
);
if
(
bodyBytes
.
Length
>
0
)
socket
.
Send
(
bodyBytes
);
}
private
static
string
ComputeWebSocketAccept
(
string
secWebSocketKey
)
{
string
combined
=
secWebSocketKey
+
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
;
byte
[]
bytes
=
Encoding
.
ASCII
.
GetBytes
(
combined
);
using
(
SHA1
sha1
=
SHA1
.
Create
())
{
return
Convert
.
ToBase64String
(
sha1
.
ComputeHash
(
bytes
));
}
}
}
}
WindBot.csproj
View file @
740de21f
This diff is collapsed.
Click to expand it.
WindBotInfo.cs
View file @
740de21f
...
...
@@ -12,6 +12,7 @@ namespace WindBot
public
string
Host
{
get
;
set
;
}
public
int
Port
{
get
;
set
;
}
public
string
HostInfo
{
get
;
set
;
}
public
string
RealIP
{
get
;
set
;
}
public
int
Version
{
get
;
set
;
}
public
int
Hand
{
get
;
set
;
}
public
bool
Debug
{
get
;
set
;
}
...
...
@@ -26,6 +27,7 @@ namespace WindBot
Host
=
"127.0.0.1"
;
Port
=
7911
;
HostInfo
=
""
;
RealIP
=
null
;
Version
=
0x1362
;
Hand
=
0
;
Debug
=
false
;
...
...
YGOSharp.Network/BinaryClient.cs
View file @
740de21f
...
...
@@ -16,7 +16,7 @@ namespace YGOSharp.Network
protected
int
HeaderSize
=
2
;
protected
bool
IsHeaderSizeIncluded
=
false
;
private
NetworkClient
_client
;
private
INetworkClient
_client
;
private
List
<
byte
>
_receiveBuffer
=
new
List
<
byte
>();
private
Queue
<
byte
[
]>
_pendingPackets
=
new
Queue
<
byte
[
]>
();
...
...
@@ -38,19 +38,20 @@ namespace YGOSharp.Network
get
{
return
_client
.
RemoteIPAddress
;
}
}
public
BinaryClient
(
NetworkClient
client
)
{
_client
=
client
;
public
BinaryClient
(
INetworkClient
client
)
{
_client
=
client
;
client
.
Connected
+=
Client_Connected
;
client
.
Disconnected
+=
Client_Disconnected
;
client
.
DataReceived
+=
Client_DataReceived
;
if
(
_client
.
IsConnected
)
{
_client
.
BeginReceive
();
}
}
client
.
Disconnected
+=
Client_Disconnected
;
client
.
DataReceived
+=
Client_DataReceived
;
if
(
_client
.
IsConnected
)
{
Client_Connected
();
_client
.
BeginReceive
();
}
}
public
void
Connect
(
IPAddress
address
,
int
port
)
{
...
...
YGOSharp.Network/INetworkClient.cs
0 → 100644
View file @
740de21f
using
System
;
using
System.Net
;
using
System.Net.Sockets
;
namespace
YGOSharp.Network
{
public
interface
INetworkClient
{
event
Action
Connected
;
event
Action
<
Exception
>
Disconnected
;
event
Action
<
byte
[
]>
DataReceived
;
bool
IsConnected
{
get
;
}
IPAddress
RemoteIPAddress
{
get
;
}
void
Initialize
(
Socket
socket
);
void
BeginConnect
(
IPAddress
address
,
int
port
);
void
BeginSend
(
byte
[]
data
);
void
BeginReceive
();
void
Close
(
Exception
error
=
null
);
}
}
YGOSharp.Network/NetworkClient.cs
View file @
740de21f
...
...
@@ -2,10 +2,10 @@
using
System.Net
;
using
System.Net.Sockets
;
namespace
YGOSharp.Network
{
public
class
NetworkClient
{
namespace
YGOSharp.Network
{
public
class
NetworkClient
:
INetworkClient
{
public
event
Action
Connected
;
public
event
Action
<
Exception
>
Disconnected
;
public
event
Action
<
byte
[
]>
DataReceived
;
...
...
YGOSharp.Network/ReverseWsClient.cs
0 → 100644
View file @
740de21f
using
System
;
using
System.IO
;
using
System.Net
;
using
System.Net.Sockets
;
using
System.Net.WebSockets
;
using
System.Threading
;
namespace
YGOSharp.Network
{
public
class
ReverseWsClient
:
INetworkClient
{
public
event
Action
Connected
;
public
event
Action
<
Exception
>
Disconnected
;
public
event
Action
<
byte
[
]>
DataReceived
;
public
bool
IsConnected
{
get
;
private
set
;
}
public
IPAddress
RemoteIPAddress
{
get
;
private
set
;
}
private
readonly
WebSocket
_socket
;
private
int
_receiveStarted
;
private
int
_connectedRaised
;
private
bool
_isClosed
;
private
readonly
object
_closeLock
=
new
object
();
public
ReverseWsClient
(
WebSocket
socket
,
IPAddress
remoteIPAddress
=
null
)
{
_socket
=
socket
;
RemoteIPAddress
=
remoteIPAddress
??
IPAddress
.
None
;
IsConnected
=
socket
!=
null
&&
socket
.
State
==
WebSocketState
.
Open
;
}
public
void
Initialize
(
Socket
socket
)
{
throw
new
NotSupportedException
(
"ReverseWsClient does not support Initialize(Socket)."
);
}
public
void
BeginConnect
(
IPAddress
address
,
int
port
)
{
throw
new
NotSupportedException
(
"ReverseWsClient does not support BeginConnect(IPAddress, int)."
);
}
public
void
BeginSend
(
byte
[]
data
)
{
if
(
_isClosed
)
return
;
try
{
if
(
_socket
.
State
!=
WebSocketState
.
Open
)
{
Close
();
return
;
}
_socket
.
SendAsync
(
new
ArraySegment
<
byte
>(
data
),
WebSocketMessageType
.
Binary
,
true
,
CancellationToken
.
None
)
.
GetAwaiter
().
GetResult
();
}
catch
(
Exception
ex
)
{
Close
(
ex
);
}
}
public
void
BeginReceive
()
{
if
(
IsConnected
&&
Interlocked
.
Exchange
(
ref
_connectedRaised
,
1
)
==
0
)
Connected
?.
Invoke
();
if
(
Interlocked
.
Exchange
(
ref
_receiveStarted
,
1
)
!=
0
)
return
;
ThreadPool
.
QueueUserWorkItem
(
delegate
{
ReceiveLoop
();
});
}
public
void
Close
(
Exception
error
=
null
)
{
lock
(
_closeLock
)
{
if
(
_isClosed
)
return
;
_isClosed
=
true
;
}
try
{
if
(
_socket
.
State
==
WebSocketState
.
Open
||
_socket
.
State
==
WebSocketState
.
CloseReceived
)
{
_socket
.
CloseAsync
(
WebSocketCloseStatus
.
NormalClosure
,
"closed"
,
CancellationToken
.
None
)
.
GetAwaiter
().
GetResult
();
}
else
{
_socket
.
Abort
();
}
}
catch
{
}
finally
{
IsConnected
=
false
;
Disconnected
?.
Invoke
(
error
);
}
}
private
void
ReceiveLoop
()
{
byte
[]
buffer
=
new
byte
[
4096
];
while
(!
_isClosed
)
{
try
{
WebSocketReceiveResult
result
;
using
(
MemoryStream
stream
=
new
MemoryStream
())
{
do
{
result
=
_socket
.
ReceiveAsync
(
new
ArraySegment
<
byte
>(
buffer
),
CancellationToken
.
None
)
.
GetAwaiter
().
GetResult
();
if
(
result
.
MessageType
==
WebSocketMessageType
.
Close
)
{
Close
();
return
;
}
if
(
result
.
MessageType
!=
WebSocketMessageType
.
Binary
)
{
Close
(
new
Exception
(
"ReverseWsClient only supports binary frames."
));
return
;
}
if
(
result
.
Count
>
0
)
stream
.
Write
(
buffer
,
0
,
result
.
Count
);
}
while
(!
result
.
EndOfMessage
);
DataReceived
?.
Invoke
(
stream
.
ToArray
());
}
}
catch
(
Exception
ex
)
{
Close
(
ex
);
return
;
}
}
}
}
}
YGOSharp.Network/ReverseWsSocketClient.cs
0 → 100644
View file @
740de21f
using
System
;
using
System.IO
;
using
System.Net
;
using
System.Net.Sockets
;
using
System.Threading
;
namespace
YGOSharp.Network
{
public
class
ReverseWsSocketClient
:
INetworkClient
{
public
event
Action
Connected
;
public
event
Action
<
Exception
>
Disconnected
;
public
event
Action
<
byte
[
]>
DataReceived
;
public
bool
IsConnected
{
get
;
private
set
;
}
public
IPAddress
RemoteIPAddress
{
get
;
private
set
;
}
private
readonly
Socket
_socket
;
private
readonly
NetworkStream
_stream
;
private
readonly
object
_sendLock
=
new
object
();
private
readonly
object
_closeLock
=
new
object
();
private
bool
_isClosed
;
private
int
_receiveStarted
;
private
int
_connectedRaised
;
private
MemoryStream
_fragmentBuffer
;
private
byte
_fragmentOpcode
;
public
ReverseWsSocketClient
(
Socket
socket
)
{
_socket
=
socket
;
_stream
=
new
NetworkStream
(
socket
,
false
);
IPEndPoint
remote
=
socket
.
RemoteEndPoint
as
IPEndPoint
;
RemoteIPAddress
=
remote
!=
null
?
remote
.
Address
:
IPAddress
.
None
;
IsConnected
=
true
;
}
public
void
Initialize
(
Socket
socket
)
{
throw
new
NotSupportedException
(
"ReverseWsSocketClient does not support Initialize(Socket)."
);
}
public
void
BeginConnect
(
IPAddress
address
,
int
port
)
{
throw
new
NotSupportedException
(
"ReverseWsSocketClient does not support BeginConnect(IPAddress, int)."
);
}
public
void
BeginSend
(
byte
[]
data
)
{
if
(
_isClosed
)
return
;
try
{
SendFrame
(
0x2
,
data
??
new
byte
[
0
]);
}
catch
(
Exception
ex
)
{
Close
(
ex
);
}
}
public
void
BeginReceive
()
{
if
(
IsConnected
&&
Interlocked
.
Exchange
(
ref
_connectedRaised
,
1
)
==
0
)
Connected
?.
Invoke
();
if
(
Interlocked
.
Exchange
(
ref
_receiveStarted
,
1
)
!=
0
)
return
;
ThreadPool
.
QueueUserWorkItem
(
delegate
{
ReceiveLoop
();
});
}
public
void
Close
(
Exception
error
=
null
)
{
lock
(
_closeLock
)
{
if
(
_isClosed
)
return
;
_isClosed
=
true
;
}
IsConnected
=
false
;
try
{
SendFrame
(
0x8
,
new
byte
[
0
]);
}
catch
{
}
try
{
_socket
.
Shutdown
(
SocketShutdown
.
Both
);
}
catch
{
}
try
{
_socket
.
Close
();
}
catch
{
}
Disconnected
?.
Invoke
(
error
);
}
private
void
ReceiveLoop
()
{
while
(!
_isClosed
)
{
try
{
int
b0
=
_stream
.
ReadByte
();
if
(
b0
<
0
)
{
Close
();
return
;
}
int
b1
=
_stream
.
ReadByte
();
if
(
b1
<
0
)
{
Close
();
return
;
}
bool
fin
=
(
b0
&
0x80
)
!=
0
;
byte
opcode
=
(
byte
)(
b0
&
0x0F
);
bool
masked
=
(
b1
&
0x80
)
!=
0
;
ulong
payloadLength
=
(
ulong
)(
b1
&
0x7F
);
if
(
payloadLength
==
126
)
{
byte
[]
ext
=
ReadExact
(
2
);
payloadLength
=
(
ulong
)((
ext
[
0
]
<<
8
)
|
ext
[
1
]);
}
else
if
(
payloadLength
==
127
)
{
byte
[]
ext
=
ReadExact
(
8
);
payloadLength
=
((
ulong
)
ext
[
0
]
<<
56
)
|
((
ulong
)
ext
[
1
]
<<
48
)
|
((
ulong
)
ext
[
2
]
<<
40
)
|
((
ulong
)
ext
[
3
]
<<
32
)
|
((
ulong
)
ext
[
4
]
<<
24
)
|
((
ulong
)
ext
[
5
]
<<
16
)
|
((
ulong
)
ext
[
6
]
<<
8
)
|
ext
[
7
];
}
if
(
payloadLength
>
int
.
MaxValue
)
{
Close
(
new
Exception
(
"Reverse WS frame is too large."
));
return
;
}
byte
[]
maskKey
=
null
;
if
(
masked
)
maskKey
=
ReadExact
(
4
);
byte
[]
payload
=
ReadExact
((
int
)
payloadLength
);
if
(
masked
&&
payloadLength
>
0
)
{
for
(
int
i
=
0
;
i
<
payload
.
Length
;
++
i
)
payload
[
i
]
=
(
byte
)(
payload
[
i
]
^
maskKey
[
i
%
4
]);
}
if
(
opcode
==
0x8
)
{
Close
();
return
;
}
if
(
opcode
==
0x9
)
{
SendFrame
(
0xA
,
payload
);
continue
;
}
if
(
opcode
==
0xA
)
continue
;
if
(
opcode
==
0x2
)
{
if
(
fin
)
{
DataReceived
?.
Invoke
(
payload
);
}
else
{
_fragmentOpcode
=
opcode
;
_fragmentBuffer
=
new
MemoryStream
();
_fragmentBuffer
.
Write
(
payload
,
0
,
payload
.
Length
);
}
continue
;
}
if
(
opcode
==
0x0
)
{
if
(
_fragmentBuffer
==
null
)
{
Close
(
new
Exception
(
"Invalid continuation frame."
));
return
;
}
_fragmentBuffer
.
Write
(
payload
,
0
,
payload
.
Length
);
if
(
fin
)
{
if
(
_fragmentOpcode
==
0x2
)
DataReceived
?.
Invoke
(
_fragmentBuffer
.
ToArray
());
_fragmentBuffer
.
Dispose
();
_fragmentBuffer
=
null
;
_fragmentOpcode
=
0
;
}
continue
;
}
if
(
opcode
==
0x1
)
{
Close
(
new
Exception
(
"Reverse WS only supports binary frames."
));
return
;
}
}
catch
(
Exception
ex
)
{
Close
(
ex
);
return
;
}
}
}
private
byte
[]
ReadExact
(
int
count
)
{
byte
[]
data
=
new
byte
[
count
];
int
offset
=
0
;
while
(
offset
<
count
)
{
int
read
=
_stream
.
Read
(
data
,
offset
,
count
-
offset
);
if
(
read
<=
0
)
throw
new
IOException
(
"Socket closed."
);
offset
+=
read
;
}
return
data
;
}
private
void
SendFrame
(
byte
opcode
,
byte
[]
payload
)
{
if
(
payload
==
null
)
payload
=
new
byte
[
0
];
lock
(
_sendLock
)
{
if
(
_isClosed
)
return
;
using
(
MemoryStream
frame
=
new
MemoryStream
())
{
frame
.
WriteByte
((
byte
)(
0x80
|
(
opcode
&
0x0F
)));
int
len
=
payload
.
Length
;
if
(
len
<=
125
)
{
frame
.
WriteByte
((
byte
)
len
);
}
else
if
(
len
<=
ushort
.
MaxValue
)
{
frame
.
WriteByte
(
126
);
frame
.
WriteByte
((
byte
)((
len
>>
8
)
&
0xFF
));
frame
.
WriteByte
((
byte
)(
len
&
0xFF
));
}
else
{
frame
.
WriteByte
(
127
);
ulong
l
=
(
ulong
)
len
;
frame
.
WriteByte
((
byte
)((
l
>>
56
)
&
0xFF
));
frame
.
WriteByte
((
byte
)((
l
>>
48
)
&
0xFF
));
frame
.
WriteByte
((
byte
)((
l
>>
40
)
&
0xFF
));
frame
.
WriteByte
((
byte
)((
l
>>
32
)
&
0xFF
));
frame
.
WriteByte
((
byte
)((
l
>>
24
)
&
0xFF
));
frame
.
WriteByte
((
byte
)((
l
>>
16
)
&
0xFF
));
frame
.
WriteByte
((
byte
)((
l
>>
8
)
&
0xFF
));
frame
.
WriteByte
((
byte
)(
l
&
0xFF
));
}
if
(
len
>
0
)
frame
.
Write
(
payload
,
0
,
len
);
byte
[]
bytes
=
frame
.
ToArray
();
_stream
.
Write
(
bytes
,
0
,
bytes
.
Length
);
_stream
.
Flush
();
}
}
}
}
}
YGOSharp.Network/WsClient.cs
0 → 100644
View file @
740de21f
using
System
;
using
System.IO
;
using
System.Net
;
using
System.Net.Sockets
;
using
System.Net.WebSockets
;
using
System.Threading
;
namespace
YGOSharp.Network
{
public
class
WsClient
:
INetworkClient
{
public
event
Action
Connected
;
public
event
Action
<
Exception
>
Disconnected
;
public
event
Action
<
byte
[
]>
DataReceived
;
public
bool
IsConnected
{
get
;
private
set
;
}
public
IPAddress
RemoteIPAddress
{
get
;
private
set
;
}
private
readonly
Uri
_uri
;
private
readonly
ClientWebSocket
_socket
;
private
int
_receiveStarted
;
private
bool
_isClosed
;
private
readonly
object
_closeLock
=
new
object
();
public
WsClient
(
Uri
uri
)
{
_uri
=
uri
;
_socket
=
new
ClientWebSocket
();
RemoteIPAddress
=
IPAddress
.
None
;
}
public
void
StartConnect
()
{
ThreadPool
.
QueueUserWorkItem
(
delegate
{
ConnectInternal
();
});
}
public
void
Initialize
(
Socket
socket
)
{
throw
new
NotSupportedException
(
"WsClient does not support Initialize(Socket)."
);
}
public
void
BeginConnect
(
IPAddress
address
,
int
port
)
{
throw
new
NotSupportedException
(
"WsClient does not support BeginConnect(IPAddress, int)."
);
}
public
void
BeginSend
(
byte
[]
data
)
{
if
(
_isClosed
)
return
;
try
{
if
(
_socket
.
State
!=
WebSocketState
.
Open
)
{
Close
();
return
;
}
_socket
.
SendAsync
(
new
ArraySegment
<
byte
>(
data
),
WebSocketMessageType
.
Binary
,
true
,
CancellationToken
.
None
)
.
GetAwaiter
().
GetResult
();
}
catch
(
Exception
ex
)
{
Close
(
ex
);
}
}
public
void
BeginReceive
()
{
if
(
Interlocked
.
Exchange
(
ref
_receiveStarted
,
1
)
!=
0
)
return
;
ThreadPool
.
QueueUserWorkItem
(
delegate
{
ReceiveLoop
();
});
}
public
void
Close
(
Exception
error
=
null
)
{
lock
(
_closeLock
)
{
if
(
_isClosed
)
return
;
_isClosed
=
true
;
}
try
{
if
(
_socket
.
State
==
WebSocketState
.
Open
||
_socket
.
State
==
WebSocketState
.
CloseReceived
)
{
_socket
.
CloseAsync
(
WebSocketCloseStatus
.
NormalClosure
,
"closed"
,
CancellationToken
.
None
)
.
GetAwaiter
().
GetResult
();
}
else
{
_socket
.
Abort
();
}
}
catch
{
}
finally
{
_socket
.
Dispose
();
IsConnected
=
false
;
Disconnected
?.
Invoke
(
error
);
}
}
private
void
ConnectInternal
()
{
try
{
_socket
.
ConnectAsync
(
_uri
,
CancellationToken
.
None
).
GetAwaiter
().
GetResult
();
IsConnected
=
true
;
try
{
IPAddress
parsedIp
;
if
(
IPAddress
.
TryParse
(
_uri
.
Host
,
out
parsedIp
))
RemoteIPAddress
=
parsedIp
;
}
catch
{
}
Connected
?.
Invoke
();
BeginReceive
();
}
catch
(
Exception
ex
)
{
Close
(
ex
);
}
}
private
void
ReceiveLoop
()
{
byte
[]
buffer
=
new
byte
[
4096
];
while
(!
_isClosed
)
{
try
{
WebSocketReceiveResult
result
;
using
(
MemoryStream
stream
=
new
MemoryStream
())
{
do
{
result
=
_socket
.
ReceiveAsync
(
new
ArraySegment
<
byte
>(
buffer
),
CancellationToken
.
None
)
.
GetAwaiter
().
GetResult
();
if
(
result
.
MessageType
==
WebSocketMessageType
.
Close
)
{
Close
();
return
;
}
if
(
result
.
MessageType
!=
WebSocketMessageType
.
Binary
)
{
Close
(
new
Exception
(
"WsClient only supports binary frames."
));
return
;
}
if
(
result
.
Count
>
0
)
stream
.
Write
(
buffer
,
0
,
result
.
Count
);
}
while
(!
result
.
EndOfMessage
);
DataReceived
?.
Invoke
(
stream
.
ToArray
());
}
}
catch
(
Exception
ex
)
{
Close
(
ex
);
return
;
}
}
}
}
}
YGOSharp.Network/YGOClient.cs
View file @
740de21f
...
...
@@ -5,15 +5,15 @@ namespace YGOSharp.Network
{
public
class
YGOClient
:
BinaryClient
{
public
YGOClient
()
:
base
(
new
NetworkClient
())
{
}
public
YGOClient
(
NetworkClient
client
)
:
base
(
client
)
{
}
public
YGOClient
()
:
base
(
new
NetworkClient
())
{
}
public
YGOClient
(
INetworkClient
client
)
:
base
(
client
)
{
}
public
void
Send
(
BinaryWriter
writer
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment