Compare commits

...

136 Commits
0.0.1 ... 0.0.5

Author SHA1 Message Date
clusterfack
f227b3abc0 Adds grid to snap grid placement manager (#384)
* Adds grid to snap grid placement manager
Fixes drawline

* Done
2017-08-26 19:21:02 +02:00
Acruid
3920da2d9f Fixes moonwalk bug and inverted up/down controls. (#380) 2017-08-26 19:20:56 +02:00
clusterfack
bfbb31aa58 Snapgrid placement (#377)
* Snapgrid placement

* REEEEEEEEEEEE world space
2017-08-26 09:33:41 +02:00
clusterfack
799be40018 Fixes scrollable containers (#381) 2017-08-26 09:33:33 +02:00
wixoaGit
152eb8dd19 Ignore outdated state update messages (#383) 2017-08-26 09:33:23 +02:00
wixoaGit
c168c0fde1 Move client GameState management (#375)
* Move client GameState management

* Remove unnecessary sequence sending

* Move resolves to dependencies, set CurrentState instead of finding it
2017-08-25 21:21:14 +02:00
clusterfack
af3c90e2a8 Removes bad shutdown (#376)
* Undoes this

* Move it to the entity manager shutdown
2017-08-25 21:20:32 +02:00
clusterfack
12e6ef88c3 Makes Walls Entities, removes tile walls (#373)
* Makes walls into entities

* Fix the align tile empty placement mode, holy shit collision bounds is fucked

* Make shadowcasting from walls work again

* Fix lights on tiles
2017-08-24 09:38:28 -06:00
clusterfack
28d74cec56 Shifts down the mobs bounding box by 0.6 tiles. (#374) 2017-08-24 14:40:28 +02:00
Pieter-Jan Briers
02efd0b41a Made the git hooks history tolerant. (#372)
If its python file doesn't exist the hooks don't run.
2017-08-23 17:13:43 -05:00
Pieter-Jan Briers
5bf337479f Fix UnknowPrototypeException typo. (#371) 2017-08-23 16:02:36 -05:00
clusterfack
3816cdb3e4 Tiles Helper Functions (#370)
* Creates a helper function for making tile indices into tile refs, and a function to get a tileref one step in any particular direction of another tileref.

* Gottem

* k
2017-08-23 11:08:42 +02:00
Pieter-Jan Briers
e54339bb5c Make null data nodes on prototypes not call LoadData() on spawned entities. (#368) 2017-08-23 07:53:31 +02:00
Pieter-Jan Briers
98d867393c Update NUnit package version. (#367)
* Update NUnit package version.

* Let's not have Travis blow up.

* And I still messed the HintPath up.
2017-08-23 07:53:16 +02:00
Pieter-Jan Briers
8dd9a3c097 Content prefixes for the reflection manager. (#369)
Allows doing class: Prototypes.DiscoBall instead of Content.Client.Prototypes.DiscoBall
2017-08-23 07:53:04 +02:00
Pieter-Jan Briers
61d30be6b3 Lighting API cleanup. (#366) 2017-08-22 07:59:13 -06:00
Pieter-Jan Briers
7aa85c0b0c ColorUtils extensions, fixes and tests. (#365)
* Color4 extensions for byte color parts, fix WithAlpha with byte.

* Unit tests and bug fix for FromHex.
2017-08-22 07:38:29 -06:00
Pieter-Jan Briers
3574395843 Fix the entity spawn panel throwing NotImplementedException, Color -> Color4 (#364)
* Fix the entity spawn panel throwing NotImplementedException.

* More rabbit hole Color conversions.
2017-08-22 07:37:04 -06:00
Pieter-Jan Briers
13b333dcf6 Fix entity initialization. (#363) 2017-08-21 15:37:04 -06:00
Pieter-Jan Briers
bead08fc7c Fix prototype class inheritance. (#362) 2017-08-21 15:36:34 -06:00
Pieter-Jan Briers
073fb20b66 Adds a per-frame virtual entity update method. (#361) 2017-08-20 19:39:42 -06:00
Pieter-Jan Briers
7adca8280c Remove some IComponent type bounds, component shutdown event. (#360)
* Remove some IComponent type bounds, component shutdown event.

Methods like GetComponent<T> don't require T to implement IComponent anymore.

Added an event to IComponent that is invoked whenever the component is shut down.

* Always at least compile your stuff kids.
2017-08-20 19:38:20 -06:00
Gilles Coremans
61183eaba3 Fixes a sonar bug. (#359) 2017-08-20 19:37:08 -06:00
wixoaGit
477b0460e8 Implement MsgEntity (#358)
* Implement MsgEntity

* Move MsgEntity registration to GameController

* Fix serializer usage
2017-08-20 19:36:33 -06:00
Pieter-Jan Briers
dd18105c92 Remove SFML from shared and server. (#355)
* Remove SFML from shared and server.

* Fix unit tests.
2017-08-20 11:07:35 -06:00
Silver
585f2467c2 Update Linux Readme (#357) 2017-08-19 23:13:12 -06:00
Pieter-Jan Briers
0c3e4ea495 Allows inputting the server port on the client. (#354) 2017-08-19 20:19:00 -06:00
Pieter-Jan Briers
afe0c95cea Fix a race condition in collidablecomponent.OnAdd (#353) 2017-08-19 20:17:11 -06:00
PJB3005
e84f072278 Update submodule to hopefully disable SFML Sonar. 2017-08-19 13:55:31 +02:00
PJB3005
ca0bbab8c0 Attempt two. 2017-08-19 13:43:46 +02:00
PJB3005
6530b8806c Attempt to ignore SFML on Sonar 2017-08-19 13:35:57 +02:00
Pieter-Jan Briers
294057e3a1 Fix resource copying on the client. (#352) 2017-08-19 11:10:55 +02:00
Pieter-Jan Briers
a57abb2ca7 Move to our own SFML.NET fork with a submodule. (#351)
* Remove SFML binaries in repo.

* Add SFML.NET submodule from our fork.

* Get build checker for submodule handling.

* Fix project references. Remove SFML from non-client.

* Make it work!

* Automatically fetch Windows CSFML natives.

* Fix project file now I'm done debugging.

* Fix project references.

* Fix CI

* Now if only I didn't typo...

* Fix Travis too

* Fix lighting.

* Move shaders over to new API.

* Fix some obsoletions.

* Improve SetUniformArray casts
2017-08-19 10:28:39 +02:00
Pieter-Jan Briers
f0d6680977 Fix accidental integer division. (#349)
AnimatedSpriteComponent.AABB used integer division, fixed now.
2017-08-19 10:23:19 +02:00
Pieter-Jan Briers
49f7b987f5 Refactor entity initialization and fix doc comments. (#346)
Entities now initialize in a few stages. First all their components are added, then entity.PreInitialize() is called, then all components are initialized, then entity.Initialize() is called.

This multi-level initialization system should hopefully be more powerful and make things like prefetching components easier.

Also what the crap WAS the old init system even?
2017-08-19 10:19:22 +02:00
clusterfack
444d0847b6 Fixing the current placement system (#347)
* WIP: Fixing the current placement system

* Placement system sprites come back pls

* HE TURNED THE FREAKING SPRITES GAY

* TECHNICALLY THIS MAKES THE SPRITES NOT GAY

* Mostly Shows sprites correctly

* Finished this bulllllshit

* Rename some things
2017-08-19 08:59:57 +02:00
clusterfack
005bd9e95c Containers (#339)
* #Part 1

Created basic container dictionary and framework for transform parenting

##Work Left until completion

1. Registering containers through a container manager
2. Registering onmove events onto the parents event handler on insertion
3. Removing onmove events from the parent event handler on removal
4. An event called upon insertion and removal from storage objects
5. Redirecting transform getters to use the uppermost parents transform

* Part 2

Creates some basic code for removing entities generally from other entities
Creates code for creating containers
Adds some basic logic for parents overriding transform getters
Adds logic to prevent circular references
Makes Container an abstract class for things to create their own versions from

* Moves container classes to shared so they can be registered on server and client
Registers container classes on server and client
Makes ContainerManager get created properly by the component factory

* Adds missing } and finalizes csproj changes

* Add exception for double containers on one component

* Makes sure parents are attached and detached at the correct time

* Address some issues, cause others perhaps

* Rename Files
Changes Container Class Inheritance
Vague stuff

* Move Icontainer and Icontainermanagercomponent to ss14 server
Make servertransform interface for parents to be serverside only
Deregister containermanager from the client IOC

* Remove Icontainer And Icontainermanager from shared csproj

* Fix

* To get git to recognize this I have to delete them

* And then readd the files, fuck you git

* Be specific you daft cunt
2017-08-18 11:18:42 -06:00
Pieter-Jan Briers
dfcf0349ca Fix F2 on Unix, collision bug and join crash. (#348)
* Make F2 not crash on Unix, fix join crash.

* Fix collision when moving to the bottom or right.

The bug was caused by 1d5ca223de. Collision adds to the AABB's Left and Top properties to do offsets, but when moving to OpenTK, this stopped working, as Right and Bottom now had to be offset too. I missed this, and as such, your hitbox is smaller when moving to the bottom or right. This allows you to clip into walls, but when you're in you can't move the other direction, as your hitbox is also *larger* when moving to the top or left.
2017-08-18 13:07:51 +02:00
Matt Smith
45a7a809db Reduce code complexity (#345) 2017-08-18 10:54:35 +02:00
Acruid
48852c5ee8 Grid Placement Fix (#344)
* Physics works again.

* forgot some SFML stuff.

* Cluwnelib does not depend on 1.0m = 1 tile anymore.

* Fixed bug with tile placement in wrong spot.
Fixed blue box issue.
2017-08-17 15:42:44 -06:00
Acruid
60ac4a9e26 Collision Fix (#340)
* Physics works again.

* forgot some SFML stuff.
2017-08-17 23:20:12 +02:00
h3half
5351f581f1 Removes commented-out code (#343)
Removes most of the commented-out code from sonarqube. There's
definitely more hiding throughout the codebase, but this should be most
of it.
2017-08-17 11:58:37 +02:00
Pieter-Jan Briers
e719dd1440 Make the lighting toggle properly disable all lighting rendering. (#342) 2017-08-16 12:38:55 -06:00
Pieter-Jan Briers
1d5ca223de Replace SFML math types with OpenTK and custom ones. (#341)
* Attempt to messily convert the codebase to use OpenTK math.

* Vector2f is gone. It compiles!

* Fix unit tests.

* Goodbye FloatRect

* Goodbye IntRect, say hello to Box2i.
2017-08-16 11:22:48 -06:00
Silver
3170552b3d Rename LICENSE.TXT to LICENSE-CODE.TXT 2017-08-15 14:44:40 -06:00
dylanstrategie
18692e21a4 Fix issues with Menu Window (#336)
* Fix issues with Menu Window

* Requested changes
2017-08-15 14:17:57 -06:00
Acruid
ae0ccc72b4 Map Manager Refactor (#338)
* Merged Client and Server Map interfaces into a Shared version.
Merged Client and Server MapManager system into a shared version.

* Added some encapsulation to the map system.
Added doc comments to client and server classes.
It compiles, but it DOES NOT WORK.

* Now with 89% more encapsulation.
Added a MapGrid object to the MapManager.

STILL A BUGGY MESS!

* Rendering bugs were fixed.

* Fixed alignment bug with GetTilesIntersecting.

* Cleanup the interfaces a bit.

* Code update.

* Merged client/server MapNetworkManager into shared version.

* Nightly work. Compiles, does not run.

* Shared networking looks like it works.

* Cleanup and remove SFML.
2017-08-14 17:30:57 -06:00
Pieter-Jan Briers
f5eb8cd55b MSBuild MacOS handling. (#337)
* Makes MSBuild distinguish Linux and MacOS.

* Fix TargetOS

* Copy mac natives during build.

* Fix travis

* Fix typo.
2017-08-14 18:54:46 +02:00
Pieter-Jan Briers
db1c71994b Give content access to OpenTK Math and Color types. (#335) 2017-08-14 18:23:46 +02:00
Silver
96df096303 Revert "Gives content access to OpenTK math and color types. (#332)" (#334)
This reverts commit 9669221d37.
2017-08-13 21:34:56 +02:00
Pieter-Jan Briers
9669221d37 Gives content access to OpenTK math and color types. (#332) 2017-08-13 13:33:32 -06:00
Pieter-Jan Briers
480ed6d74c Fixing some compile warnings. (#331)
All the obsoletions from netmessages will be for another PR.
2017-08-13 11:30:23 -06:00
Dan
1d2614d75f Refactor align modes (#330)
* Refactor align modes

* Remove some unnessecary lines
2017-08-13 14:30:55 +02:00
Pieter-Jan Briers
1c5f540751 Remove component instantiation messages. (#324) 2017-08-13 00:46:00 +02:00
Pieter-Jan Briers
f4fe79346e Inverse entity queries. (#322)
* Inverse entity queries.

They now effectively behave like Func<IEntity, bool>.
The actual matching logic is now done on entity queries.

They've also been abstracted, as such there are now multiple entity
query types to pick from.

Also generic clean up.

* Fix doc comment on the exclude list.

* Use Predicate<> instead of Func<>

* Fix compile, Predictae<> doesn't work
2017-08-13 00:31:58 +02:00
Acruid
cb5bfbb3da Component Rework (#325)
* Cleans up base Component.

* Clean up PhysicsComponent.
Merge VelocityComponent with PhysicsComponent.

* Renamed HitBoxComponent to BoundingBoxComponent.
Cleaned up BoundingBoxComponent.
Added Box2 to the serializer and SfmlCompatibility.

* Serverside movement works.

* Removed DirectionComponent.
Everything is broke, time to sleep.

* Removed BasicMoverComponent and SlaveMoverComponent.
Added Parenting skeleton to TransformComponent.

* Move ClickAbleComponent to Input.
Fixed lights.

* More general cleanup.

* Move Physics system into Shared.

* Removed ColliderComponent.

* Fixed AnimatedSprite Rotation.

* Added Vector2i as a type.

* Fixes StackOverflow bug by just not using SFML.Vector2f.

* Should fix Build issues.

* Removed pointless server console resizing.
Fixed Unit tests working in VS.
Updated PrototypeManager_Test.cs tests.

* Adds SS14.Shared/Maths/VecMath.cs helper functions.

* Switched to Angle type.
2017-08-13 00:10:06 +02:00
Pieter-Jan Briers
f41050583e Bit of unit test stuff. (#326)
* Unit test for #294

* Remove empty warning tests.
2017-08-13 00:09:42 +02:00
clusterfack
85fa167e17 Fixes Exceptions on Rejoining (#327)
* Fix server crash on rejoin

* Makes it possible to rejoin without exception

* Proper fix by culling ALL the component lists when the entity manager shuts down
2017-08-12 10:25:33 +02:00
Pieter-Jan Briers
b9723b6504 Content repo can now define console commands. (#321) 2017-08-11 15:44:47 -06:00
Pieter-Jan Briers
2e64de57c6 Makes the main menu pretty. (#320)
>duct tape logo

INSTAMERGE
2017-08-11 14:37:31 -06:00
Pieter-Jan Briers
feaf4160e8 Load unatlassed images from disk as sprites (#319)
This'll allow content to add sprites.
2017-08-11 08:23:23 +02:00
Pieter-Jan Briers
93fd0014b4 Fix content entry invocations, whitelist types. (#317) 2017-08-09 21:50:45 -06:00
Pieter-Jan Briers
22f07e3649 Adds OS targeting to the build setup (#316)
* Adds OS targeting to the build setup

Will make cross platform builds (Linux -> Windows) easier.

* Rename Windows_NT to Windows and explicitly do Linux.
2017-08-10 00:04:31 +02:00
clusterfack
398a3917dc Returns runclient and runserver 2017-08-09 08:26:08 -05:00
Pieter-Jan Briers
04fa16902f Fix SFML memory leak. (#315) 2017-08-09 07:39:19 +02:00
Jordan Brown
b0e42ef650 Removes support for resource pack passwords (#314) 2017-08-08 21:36:53 +02:00
Pieter-Jan Briers
b0d7ee16c1 Deprecate resource pack temporarily. (#312)
* Deprecate resource pack temporarily.

Temporarily being until we actually need content downloading from server.
Resources now get copied to the output directories directly.

* Move prototypes around.
2017-08-08 14:48:15 +02:00
Pieter-Jan Briers
b9ece2888b Repo cleanup (#310)
* Reduce build size by removing redundant files.

22 MB of the build output was OpenTK.xml, the doc file for it.
This commit removes a lot of files like PDB files from the build output
on Release, cutting build size down to 13 MB.

Also deleted a ton of dead third party files in Third-Party, and the two
spare copies of CSFML...

* Remove more dead files.

* Even more!

* Generic repo cleanup. Removing dead files all over.

What the hell was filesystemeditor.exe supposed to be?
2017-08-08 12:47:31 +02:00
Pieter-Jan Briers
d1b29a6f94 Update NetSerializer, move to NuGet with it. (#311)
NetSerializer has been updated and moved to the latest version. This
means that the static nonsense it previously had is fixed and we can
properly use ISS14Serializer.
2017-08-08 12:47:18 +02:00
clusterfack
a43da12f9b Fixes a rare out of bounds exception (#313)
* Fixes a rare crash

Fixes a crash that occurs from the following
>return AngleDirections[(int)Math.Floor(angle/45f)];

System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'

That occurs most likely due to a float rounding error

* Apparently this works fine
2017-08-08 11:51:38 +02:00
Pieter-Jan Briers
4607a106d0 Of course it works a lot different on the client. 2017-08-06 14:47:20 +02:00
Pieter-Jan Briers
de9fd0cbaf Fix resource pack on client, regression in #306 2017-08-06 12:56:12 +02:00
Acruid
2e3315cd39 CompState Cleanup (#308)
* Changed GameObjects.Component namespace back to GameObjects.Components namespace to prevent colliding with GameObjects.Component class.

* Changed IComponent.HandleComponentState to use polymorphism instead of dynamic keyword.
Cleaned up nesting in each implementation of IComponent.HandleComponentState.

* Moved all shared ComponentStates to the SS14.Shared.GameObjects namespace to be more in line with client & server components.
Made all ComponentStates immutable, as they should be.
2017-08-06 12:16:47 +02:00
Acruid
f7b1b9c4b6 Client Timing (#309)
* Appease lord Sonar.

* Client timing *should* work.
2017-08-05 17:15:33 -06:00
Pieter-Jan Briers
2572bc94c7 Increase default logging verbosity to DEBUG. (#307) 2017-08-05 23:32:57 +02:00
Pieter-Jan Briers
47f1aa6565 Unhardcode content pack locations. (#306)
* Unhardcode content pack locations.

The previous hardcoded path was causing problems for the content repo.
Now the content packs are loaded from the executable directory.
The build system copies the pack from the Resources/ folder now.

* Fix CI
2017-08-05 23:32:44 +02:00
Pieter-Jan Briers
fdde14b93e Makes client and server load shared assembly too. (#304)
Also fixes #303
2017-08-05 23:32:28 +02:00
Pieter-Jan Briers
975eea832d Clean up build targets and output dirs. (#302)
* Clean up build targets and output dirs.

All old Any CPU targets and mixed ones have been removed. They wouldn't have worked anyways due to SFML.

Cleaned up all the build output directories to be neat and clean.

* Fix Travis
2017-08-04 07:11:28 -06:00
Pieter-Jan Briers
09ecdade70 Fix sonar E rating by removing dead Lidgren code.
Specifically, dead encryption systems like DES which are useless in 2017.
2017-08-03 20:32:16 +02:00
Pieter-Jan Briers
e8b40f6091 Make references in projects solution-relative, fixing content#3. (#300)
* Make package references in project files solution-relative.

Fixes https://github.com/space-wizards/space-station-14-content/issues/3 with the SolutionDir msbuild property.

* Fix openTK post-merge
2017-08-03 08:49:57 -06:00
Acruid
9fa34d8cf8 Upgrade and Include OpenTK in all projects. (#299)
* Upgrades OpenTK 1.6 to 3.0pre in SS14.Client.Graphics project.
Adds OpenTK to all projects so that the math library is available.
Adds helper functions to SFML and OpenTK vectors for better compatibility.

* Fixed issue with OpenTK.dll.config needing to be copied to output.
2017-08-03 00:37:33 +02:00
Acruid
540825cf42 Content System + Content Assembly Loading (#295)
* Initial content system rework.

* Renamed old ResourceManager to ResourceCache.

* ResourceCache works.

* Enhanced assembly loading.
Calling LoadAssembly on the ReflectionManager breaks prototypes?

* Fixed bug with Prototypes getting erased.

* Basic namespace whitelist.

* Directory Mounting to the VFS.
More cleanup & bugfixes.

* Finished White/Black lists.

* Final Cleanup.

* Client now loads the Content.Client.dll.
Cleaned up some Logger calls.
Remove temp resource file.

* Release builds now work.

* Right, unit tests...

* Fixed Review issues.
2017-08-02 07:56:12 -06:00
clusterfack
4ae873b262 Fix the esc menu (#298)
The esc menu would previously dispose of itself if you used the X button, however this menu is meant simply to toggle invisbility on and off when you press X or esc.
Fixes #296
2017-08-02 07:53:25 -06:00
Silver
f4bceaf988 WARK 2017-08-01 08:34:30 -06:00
clusterfack
f9b27c0b21 Fixes IOC dependency system (#294) 2017-07-31 12:07:55 -06:00
Pieter-Jan Briers
0b1b9ce445 Recieve -> Receive (#290) 2017-07-26 10:34:56 +02:00
Pieter-Jan Briers
5273274f8f Moving some component API to interfaces accessible from Shared. (#289)
* Move TransformComponent to use interfaces accessible from Shared.

* Documentation and interface conversion for DirectionComponent.

* Clickable component cleaned up and Shared.

* VelocityComponent standardized.

* Update doc comments on IClientClickable
2017-07-26 10:33:59 +02:00
Pieter-Jan Briers
c8e2dcda8a Fix command line handling. (#285)
Somewhere along one of the refactors the code using it got lost.

Also made the server config be relative to the executable if unspecified, relative to the working dir when specified (through terminal for example).
2017-07-26 08:27:46 +02:00
Pieter-Jan Briers
1e5f539d50 Disable lighting by default on MacOS and Unix. (#283)
The rendering doesn't work at all and it's better that it defaults off for now. It can still be enabled with F6 in game.
2017-07-24 09:44:26 -06:00
Pieter-Jan Briers
7718d6bdc8 Makes client console darker to improve contrast and readability. (#282) 2017-07-24 09:43:38 -06:00
Pieter-Jan Briers
8cac0c1039 Remove ComponentFamily, remove "game" components. (#281)
* Remove ComponentFamily, remove "game" components.

The entire ComponentFamily system has been removed entirely.
Components now require explicit registration into the IComponentFactory.
This registration spawns the component as dummy and checks reads some data off it like name, netID...
"Reference" types can also be registered using RegisterReference<TTarget, TInterface>().
These references allow you to get components from entities too, so you can reference by interfaces.
This should allow us to inverse the direct fetching and provide a component API available from Shared.
Note that these reference CANNOT be used to spawn a new component. Only a concrete registration can.

Targeted component family messaging no longer exists.
The messaging system still exist, but it's always to ALL components.
The system should probably be killed completely later.
To fill the gap, all cases where family-targeted messaging was used, it now uses references and simple interfaces.

To reference components accross the network, the component now needs to define a "Net ID".
These net IDs should be unique (but matching between client and server).
I made a set of constants for these IDs too in NetIDs.cs

Names are no longer used in netcode (replaced by net IDs). Should reduce bandwidth usage from string names.

Because a LOT of code got in the way, most "game" content was cut. This includes (among others) items and interaction.
This had to be reimplemented in content, and most of the code was terrible anyways.

Also removed the messaging profiling system. It got in my way and I don't understand it (and I doubt anybody does).
It's in the git history if somebody cares.

Fixes #262, Fixes #250, Fixes #74.

* Attempt to fix unit tests, not yet done.

* Fix unit tests, properly.

* Remove debug message.

* Fix rendering on WearableAnimatedSprite.

It wasn't being registered as ISpriteRenderableComponent because of a typo. The ComponentFactory now detects this error too.
2017-07-24 12:27:28 +02:00
Silver
9727f67b8c Update CODEOWNERS
testing
2017-07-23 04:15:56 -06:00
Silver
348426faa1 Update CODEOWNERS
y dis no wark
2017-07-23 04:14:30 -06:00
Acruid
514efc90d5 Server Main Loop (#280)
* Basic timing class.

* Added new timing class to use as a central location for keeping track of time.
Enhanced/fixed main loop of server.

* Replaced the Win32 TimerQueue with the System.Thread.Timer version.

* Moved the timing system into IoC.

* The timer was a mistake.

* Final Cleanup.

* Fixed visual bug with the TickRate log message.
2017-07-21 17:50:53 -06:00
Jordan Brown
6d09b02d9f Replaces all TestFixture constructors with OneTimeSetUp attribute (#279)
* Replaces all TestFixture constructors with OneTimeSetUp attribute

* Make base TestFixuture use OneTimeSetUp
2017-07-20 19:43:39 +02:00
Silver
cbd4d21f3f Add Silver, PJB to Codeowners 2017-07-19 16:56:02 -06:00
Matt Smith
d9e0abd3ff Create empty CODE_OWNERS file (#270)
* Create empty CODE_OWNERS file

* Rename CODE_OWNERS to CODEOWNERS
2017-07-15 12:27:31 +02:00
Pieter-Jan Briers
8e0c57760b Fix compiler warning due to included bitstream resx. (#278) 2017-07-13 11:19:14 +02:00
Pieter-Jan Briers
a99a6af307 Band-aid the memory leak. (#277) 2017-07-12 23:56:15 +02:00
Acruid
50ae510929 Fixes Timer cleanup, so the server process does not crash when closed. (#274) 2017-07-11 23:32:19 +02:00
Pieter-Jan Briers
13ae3647be Entity prototype & YAML cleanup, prototypes unified. (#268)
* gotta go

* Work cleaning up entity prototypes.

Added handling for the data field in entity prototypes. Closes #131.
Cleaned up a bunch of the YAML handling.
Made missing components throw errors and added a (to be done) error ignore
list.

This'll allow me to unify the prototypes of client and server later.

* Unify client and server prototypes.
2017-07-11 00:44:28 +02:00
Pieter-Jan Briers
289913f14b Gives the client an application icon. (#273) 2017-07-10 23:24:51 +02:00
Acruid
e676142d72 Network system (#269)
* Saving progress of the SS14.Server.Network*, SS14.Server.Player*, and SS14.Server.SS14Server.cs rewrite.
It compiles, but does not run.

* Nightly commit, Still bugged, but compiles.
Made progress, it can now show the map!

* Runs OK, time to purge lidgren from the rest of the server.

* Finished removing most of lidgren from SS14.Server.
General cleanup of networking system.

* Merged the client NetManager into the shared version.

* Removed the giant packet processing switch.
Fixed a few misc bugs.

* Adds the INetServerManager interfaces for servers to use.
Remade the StringTable system into it's own class.
Fixed a few random annoying bugs.

* Got the OnConnecting event working, other modules can accept/deny incoming connections.
Removed the giant block of private config vars in BaseServer.

* Fixed exception issues, now networks exceptions are caught when a packet fails to deserialize.
Final housekeeping.

* More bugfixes.

* Travis fixes 1

* Travis Fixes 2

* Code review changes.
2017-07-10 21:04:08 +02:00
Pieter-Jan Briers
79af327858 Fix unit tests ran from VS. (#272)
Something NetSerializer is doing isn't working in VS. This band aids it by
moving it out of the constructor so IoC can initialize at least.
2017-07-10 19:28:02 +02:00
Pieter-Jan Briers
4adcba88f2 Remove buildResourcePack* batch files. (#259)
The files have been obsolete with the advent of the (faster) buildResourcePack.py.
There's no reason to use the old ones, and this sets Python 3.6 as a hard build dependency.
2017-07-10 06:46:12 +02:00
sood
22fa2c3f40 Updates lidgren, adds ipv6 support (#204)
* restart

* saving work

* IT LIVES

* overload method instead

* Solution file changes ala Visual Studio should make it build now
2017-07-10 01:09:23 +02:00
Acruid
001abd663c General Bugfixes (#264)
* Fixed SFML Fonts throwing StreamClosed errors.
Fixed misspelled font name causing chat box to use default font.
Fixed crash when trying to quit the client from inside a round.
Fixed? backslash issue with the resource pack path.
Removed obsolete unused Player_config.xml.

* More path separator fixing.
Fixed the player sprite moonwalking bug.

* Reverted changes of last commit.
2017-07-10 01:01:49 +02:00
sood
822070dfdc Fix hardcoded resource path preventing client from starting on Unix (#265)
* Fix hardcoded resource path preventing client from starting on Unix

* also fix this unit test path

* okay actually fix it because I'm dumb
2017-07-09 20:47:50 +02:00
sood
2f2aa40362 Add SpriteRenderer/Output to gitignore (#266) 2017-07-09 14:51:40 +02:00
Pieter-Jan Briers
04ff5b945a Whoops 2017-07-07 00:42:30 +02:00
Pieter-Jan Briers
e4b76abd46 Properly implement ISerializable on ImplementationConstructorException. 2017-07-07 00:38:09 +02:00
Pieter-Jan Briers
16ec81b77a Bunch of generic clean up following Sonar. 2017-07-07 00:17:24 +02:00
Pieter-Jan Briers
dfbef437f8 Pleasong SonarCloud a bit, fixing tons of smells. 2017-07-06 16:27:04 +02:00
Pieter-Jan Briers
8b8f2354b3 Fix build. 2017-07-06 16:00:12 +02:00
Silvertorch5
6d653f8876 Clean up 2017-07-06 01:33:49 -06:00
Silvertorch5
5a1f524e83 Rename Serverside EntityNetworkManager
EntityNetworkManager -> ServerEntityNetworkManager
2017-07-06 01:33:03 -06:00
Silvertorch5
17d22f345f Rename Clientside EntityNetworkManager
EntityNetworkManager -> ClientEntityNetworkManager
2017-07-06 01:32:28 -06:00
Pieter-Jan Briers
7df089bebe IoC property-based field injection. (#258)
* IoC Field Injection base changes. Usages not converted yet.

* Fix issues with the csproj file.

* Work on converting the client.

* We're done here. All implemented and good.
2017-07-06 01:06:52 -06:00
Acruid
eefa09f93b Unified Config (#253)
* Added new shared ConfigurationManager.
Added Configuration Variable system.
server migrated from old had-coded config system to new ConfigurationManager.

* Client migrated from old hand-coded config system to new ConfigurationManager.
STILL HAS BUGS, DOES NOT RUN, AND IT IS ALMOST MIDNIGHT.

* TOML file saving now works.
Visible bugs fixed.
Generated default *config.toml files.

* Fixed unit tests.
Fixed TomlObjectType escaping the LoadFile function.

* Fixed missing using statements in GameController.cs
Fixed bug with Directives in NetworkManager.cs

* Fixed issues from the code review.
Renamed LoadFile/SaveFile to LoadFromFile/SaveToFile because English.
Moved the Client SaveToFile to later in the shutdown stage so that it runs in all screens.

* CVar names now support nested tables.
CVar names changed to use period '.' delimiters instead of underscores '_'.
2017-06-29 17:37:56 +02:00
Pieter-Jan Briers
db63c5cc2f Update README (#247) 2017-06-20 21:27:17 +02:00
Pieter-Jan Briers
b9267af6c1 Remove automatic IoC discovery (#246)
* First bits removing IoCTarget.

* Client works.

* IoCTarget removal done on server.

* IoCTarget removal in unit tests done.

* Remove IIoCInterface and IoCTarget.
2017-06-20 12:04:27 +02:00
Pieter-Jan Briers
860a8a8e03 Revert "Add a link to the waffles board into the readme. Take Two." (#244)
* Revert "Remove IoCManager.ResolveEnumerable, ContentLoader (#236)"

This reverts commit 36a5386a3d.

* Revert "Add a link to the waffles board into the readme. (#239)"

This reverts commit 05b000bb5d.
2017-06-17 18:30:32 +02:00
Pieter-Jan Briers
36a5386a3d Remove IoCManager.ResolveEnumerable, ContentLoader (#236)
* Remove IoCManager.ResolveEnumerable, ContentLoader

IContentLoader has been replaced with IReflectionManager.
IoCManager.ResolveEnumerable<T> is now handled by
IReflectionManager.GetAllChildren<T>. IoC now solely handles services.

That was pretty bad API design on my part.

* Make ClientComponent have [Reflect(false)]

* Unit tests.
2017-06-17 17:33:51 +02:00
Pieter-Jan Briers
05b000bb5d Add a link to the waffles board into the readme. (#239) 2017-06-17 17:32:43 +02:00
Pieter-Jan Briers
2ec2a26391 Makes the .editorconfig set to utf-8-bom (#237)
Visual Studio appears extremely stubborn on saving as UTF-8 with a BOM.
2017-06-17 16:57:55 +02:00
Pieter-Jan Briers
afe3913893 Fix EOLs (#235) 2017-06-14 21:42:48 -06:00
Pieter-Jan Briers
e707138d14 Move logging to IoC. (#229) 2017-06-15 02:48:34 +02:00
Pieter-Jan Briers
9f8e9eaae2 Disable splash screen for debug builds. (#228) 2017-06-15 02:48:25 +02:00
Pieter-Jan Briers
19a8e0309c Fix prototype loader to work with case sensitivity (#231)
Didn't like case sensitive filesystems like on Linux. The prototypes
folder was "Prototypes" but it was looking for "prototypes".
2017-06-15 02:48:14 +02:00
Pieter-Jan Briers
cc3f2dc801 Fix msbuild on case sensitive file systems. (#232)
* Fix msbuild on case sensitive file systems.

* Make Travis use msbuild instead of xbuild.
2017-06-15 02:47:48 +02:00
Pieter-Jan Briers
787bb1801f Unit test Option system. (#233)
Unit tests now have some primitive options to enable systems like the
resource pack explicitly. This should speed up tests a lot on non-headless
builds.

Tests that were previously behind #if HEADLESS are now automatically
disabled instead of they require the resource pack.
2017-06-15 02:47:36 +02:00
Pieter-Jan Briers
bb06eccb87 Remove rogue NetSerializerDebug.dll (#230) 2017-06-14 21:24:52 +02:00
Pieter-Jan Briers
3e966ca778 Fixes #225 (#227) 2017-06-14 19:58:11 +02:00
Pieter-Jan Briers
86ac47841c Makes Sonar not be installed for AppVeyor PR builds. (#226) 2017-06-14 16:27:09 +02:00
Pieter-Jan Briers
dcfa7cc4bf GameObjects IoC refactor. (#224)
* Move GameController to IoC, prevent IoC circular deps.

* Make more data on the new exception constructors public.

* Client game controller done, slightly cleaner now.

* Some work on the entity manager. Primarily the entity system manager.

* More work on the entity manager.

* GameObjects IoC refactor work.

There comes a point at which you're just clicking on error messages in
VS without much thought.

* todo

* More work. Entity -> IEntity

* Hopefully done with IEntity conversions.

* Hey it compiles!

* Almost done!

* IT'S ALIVE

* Circular dependency unit test & fixing the circular dependency checker

* Test ImplementationConstructorException
2017-06-14 16:15:26 +02:00
Quantomicus
2fb9a980b8 Lighting de-lagging (#222)
* lighting delagging

* wtf

* texturecache refactor
2017-06-08 17:41:30 -06:00
915 changed files with 38017 additions and 50330 deletions

View File

@@ -12,11 +12,13 @@ pull_requests:
image: Visual Studio 2017
clone_depth: 10
install:
- cinst msbuild-sonarqube-runner
- ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { cinst msbuild-sonarqube-runner }
before_build:
- cmd: py -3.5 RUN_THIS.py
- cmd: py -3.5 Resources\buildResourcePack.py --resources-dir .\Resources --out .\Resources\ResourcePack.zip --atlas-tool .\Tools\AtlasTool.exe --no-animations --to-stderr
- cmd: nuget restore SpaceStation14.sln
- ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { MSBuild.SonarQube.Runner.exe begin /k:"ss14" /d:"sonar.host.url=https://sonarqube.com" /d:"sonar.login=$env:sonarqubekey" /d:"sonar.organization=space-wizards" }
- ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { MSBuild.SonarQube.Runner.exe begin /k:"ss14" /d:"sonar.host.url=https://sonarqube.com" /d:"sonar.login=$env:sonarqubekey" /d:"sonar.organization=space-wizards" /d:"sonar.exclusions=SFML/**"}
build:
project: SpaceStation14.sln
@@ -25,4 +27,3 @@ build:
after_build:
- ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { MSBuild.SonarQube.Runner.exe end /d:"sonar.login=$env:sonarqubekey" }
- cmd: py -3.5 Resources\buildResourcePack.py --resources-dir .\Resources --out .\Resources\ResourcePack.zip --atlas-tool .\Tools\AtlasTool.exe --no-animations --to-stderr

View File

@@ -1,4 +0,0 @@
{
"phabricator.uri": "http://phab.nexisonline.net",
"project.name": "Space Station 14"
}

View File

@@ -1,11 +1,11 @@
root = true
root = true
[*]
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
charset = utf-8
charset = utf-8-bom
[*.{csproj,xml,yml,dll.config}]
[*.{csproj,xml,yml,dll.config,targets}]
indent_size = 2

14
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,14 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners.
# These owners will be the default owners for everything in the repo.
# * @defunkt
* @PJB3005 @Silvertorch5
# Order is important. The last matching pattern has the most precedence.
# So if a pull request only touches javascript files, only these owners
# will be requested to review.
# *.js @octocat @github/js
# You can also use email addresses if you prefer.
# docs/* docs@example.com

8
.gitignore vendored
View File

@@ -52,6 +52,7 @@ _ReSharper*/
/Resources/textures/*_UserInterface.png
/Resources/textures/*.TAI
/Resources/SpriteRenderer/Ogre.log
/Resources/Spriterenderer/output
/Media
/setenv.bat
@@ -73,3 +74,10 @@ project.lock.json
# Created by NUnit.
TestResult.xml
NetSerializerDebug.dll
# We're not gonna ship Mac extlibs with the repo due to size. (11 MB)
Third-Party/extlibs/Mac/
# Or the automatically-fetched Windows natives, for that matter.
Third-Party/extlibs/Windows/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "SFML"]
path = SFML
url = https://github.com/space-wizards/SFML.Net.git

View File

@@ -11,11 +11,12 @@ before_install:
- if [ $TRAVIS_OS_NAME = osx ]; then brew update && brew install python3; fi
before_script:
- "python3 ./Resources/buildResourcePack.py --resources-dir ./Resources --out ./Resources/ResourcePack.zip --no-atlas --no-animations --to-stderr"
- "nuget restore SpaceStation14.sln"
- "python3 RUN_THIS.py"
script:
- "xbuild /p:Configuration=Release /p:HEADLESS=1 SpaceStation14.sln"
- "cd packages/NUnit.ConsoleRunner.3.6.1/tools"
- "mono --debug nunit3-console.exe ../../../SS14.UnitTesting/bin/Release/SS14.UnitTesting.dll"
- "python3 ./Resources/buildResourcePack.py --resources-dir ./Resources --out ./Resources/ResourcePack.zip --no-atlas --no-animations --to-stderr"
- "msbuild /p:Configuration=Release /p:Platform=x86 /p:HEADLESS=1 /nologo /m /p:AllowMissingMacNatives=yes SpaceStation14.sln"
- "cd packages/NUnit.ConsoleRunner.3.7.0/tools"
- "mono --debug nunit3-console.exe ../../../bin/UnitTesting/SS14.UnitTesting.dll"

1
BuildChecker/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
INSTALLED_HOOKS_VERSION

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This is a dummy .csproj file to check things like submodules.
Better this than other errors.
If you want to create this kind of file yourself, you have to create an empty .NET application,
Then strip it of everything until you have the <Project> tags.
VS refuses to load the project if you make a bare project file and use Add -> Existing Project... for some reason.
You want to handle the Build, Clean and Rebuild tasks to prevent missing task errors on build.
If you want to learn more about these kinds of things, check out Microsoft's official documentation about MSBuild:
https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
-->
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Python>python3</Python>
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
<ProjectGuid>{D0DA124B-5580-4345-A02B-9F051F78915F}</ProjectGuid>
<OutputType>Exe</OutputType>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<Target Name="Build">
<Exec Command="$(Python) git_helper.py" CustomErrorRegularExpression="^Error" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Build" />
<Target Name="Clean">
<Message Importance="low" Text="Ignoring 'Clean' target." />
</Target>
</Project>

101
BuildChecker/git_helper.py Normal file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python3
# Installs git hooks, updates them, updates submodules, that kind of thing.
import subprocess
import sys
import os
import shutil
from pathlib import Path
from typing import List
SOLUTION_PATH = Path("..") / "SpaceStation14Content.sln"
CURRENT_HOOKS_VERSION = "2" # If this doesn't match the saved version we overwrite them all.
QUIET = "--quiet" in sys.argv
NO_HOOKS = "--nohooks" in sys.argv
def run_command(command: List[str], capture: bool = False) -> subprocess.CompletedProcess:
"""
Runs a command with pretty output.
"""
text = ' '.join(command)
if not QUIET:
print("$ {}".format(text))
sys.stdout.flush()
completed = None
if capture:
completed = subprocess.run(command, cwd="..", stdout=subprocess.PIPE)
else:
completed = subprocess.run(command, cwd="..")
if completed.returncode != 0:
print("Error: command exited with code {}!".format(completed.returncode))
return completed
def update_submodules():
"""
Updates all submodules.
"""
# If the status doesn't match, force VS to reload the solution.
# status = run_command(["git", "submodule", "status"], capture=True)
run_command(["git", "submodule", "update", "--init", "--recursive"])
# status2 = run_command(["git", "submodule", "status"], capture=True)
# Something changed.
# if status.stdout != status2.stdout:
# print("Git submodules changed. Reloading solution.")
# reset_solution()
def install_hooks():
"""
Installs the necessary git hooks into .git/hooks.
"""
# Read version file.
if os.path.isfile("INSTALLED_HOOKS_VERSION"):
with open("INSTALLED_HOOKS_VERSION", "r") as f:
if f.read() == CURRENT_HOOKS_VERSION:
if not QUIET:
print("No hooks change detected.")
return
with open("INSTALLED_HOOKS_VERSION", "w") as f:
f.write(CURRENT_HOOKS_VERSION)
print("Hooks need updating.")
hooks_target_dir = Path("..")/".git"/"hooks"
hooks_source_dir = Path("hooks")
if not os.path.exists(str(hooks_target_dir)):
os.makedirs(str(hooks_target_dir))
# Clear entire tree since we need to kill deleted files too.
for filename in os.listdir(str(hooks_target_dir)):
os.remove(str(hooks_target_dir/filename))
for filename in os.listdir(str(hooks_source_dir)):
print("Copying hook {}".format(filename))
shutil.copyfile(str(hooks_source_dir/filename), str(hooks_target_dir/filename))
def reset_solution():
"""
Force VS to think the solution has been changed to prompt the user to reload it, thus fixing any load errors.
"""
with SOLUTION_PATH.open("r") as f:
content = f.read()
with SOLUTION_PATH.open("w") as f:
f.write(content)
if __name__ == '__main__':
if not NO_HOOKS:
install_hooks()
update_submodules()

View File

@@ -0,0 +1,19 @@
#!/bin/bash
gitroot=`git rev-parse --show-toplevel`
cd "$gitroot/BuildChecker"
if [ -f "git_helper.py" ]
then
if [[ `uname` == MINGW* || `uname` == CYGWIN* ]]; then
# Windows
# Can't update hooks from here because we are a hook,
# and the file is read only while it's used.
# Thanks Windows.
py -3 git_helper.py --quiet --nohooks
else
# Not Windows, so probably some other Unix thing.
python3 git_helper.py --quiet
fi
fi

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Just call post-checkout since it does the same thing.
gitroot=`git rev-parse --show-toplevel`
bash "$gitroot/.git/hooks/post-checkout"

View File

@@ -1,4 +0,0 @@
call "c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86
@echo on
call prebuild-2010.cmd
call msbuild SpaceStation14.sln /t:Build /p:Configuration=Release;Platform=x86

View File

@@ -1,10 +0,0 @@
#!/bin/bash
prebuild-nant.sh
nant
#nunit-console Bin/SS14.Test.dll -nodots -xml=NUnit.Results.xml
echo NUnit disabled.
#7za a SS14.7z Bin/*
echo Packaging disabled.

View File

@@ -1,2 +0,0 @@
cd bin\client
start SpaceStation14.exe

View File

@@ -1,2 +0,0 @@
cd bin\server
start SpaceStation14_Server.exe

View File

@@ -1,10 +1,10 @@
* The NetBuffer object is gone; instead there are NetOutgoingMessage and NetIncomingMessage objects
* No need to allocate a read buffer before calling ReadMessage
* The NetBuffer object is gone; instead there are NetOutgoingMessage and NetIncomingMessage objects
* No need to allocate a read buffer before calling ReadMessage

View File

@@ -1,113 +1,113 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<style type="text/css">
body
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
margin-left: 20px;
background-color: #dddddd;
}
td
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
}
.page
{
width: 700px;
}
.cf
{
font-family: Courier New;
font-size: 10pt;
color: black;
background: white;
padding: 16px;
border: 1px solid black;
}
.cl
{
margin: 0px;
}
.cb1
{
color: green;
}
.cb2
{
color: #2b91af;
}
.cb3
{
color: blue;
}
.cb4
{
color: #a31515;
}
</style>
<title>Peer/server discovery</title>
</head>
<body>
<table>
<tr>
<td class="page">
<h1>Peer/server discovery</h1>
<p>
Peer discovery is the process of clients detecting what servers are available. Discovery requests can be made in two ways;
locally as a broadcast, which will send a signal to all peers on your subnet. Secondly you can contact an ip address directly
and query it if a server is running.
</p>
<p>Responding to discovery requests are done in the same way regardless of how the request is made.</p>
<p>Here's how to do on the client side; ie. the side which makes a request:</p>
<div class="cf">
<pre class="cl"><span class="cb1">// Enable DiscoveryResponse messages</span></pre>
<pre class="cl">config.EnableMessageType(<span class="cb2">NetIncomingMessageType</span>.DiscoveryResponse);</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl"><span class="cb1">// Emit a discovery signal</span></pre>
<pre class="cl">Client.DiscoverLocalPeers(14242);</pre>
</div>
<p>This will send a discovery signal to your subnet; Here's how to receive the signal on the server side, and send a response back to the client:</p>
<div class="cf">
<pre class="cl"><span class="cb1">// Enable DiscoveryRequest messages</span></pre>
<pre class="cl">config.EnableMessageType(<span class="cb2">NetIncomingMessageType</span>.DiscoveryRequest);</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl"><span class="cb1">// Standard message reading loop</span></pre>
<pre class="cl"><span class="cb3">while</span> ((inc = Server.ReadMessage()) != <span class="cb1">null</span>)</pre>
<pre class="cl">{</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; <span class="cb3">switch</span> (inc.MessageType)</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; {</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DiscoveryRequest:</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb1">// Create a response and write some example data to it</span></pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb2">NetOutgoingMessage</span> response = Server.CreateMessage();</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; response.Write(<span class="cb4">&quot;My server name&quot;</span>);</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb1">// Send the response to the sender of the request</span></pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Server.SendDiscoveryResponse(response, inc.SenderEndpoint);</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">break</span>;</pre>
</div>
<p>When the response then reaches the client, you can read the data you wrote on the server:</p>
<div class="cf">
<pre class="cl"><span class="cb1">// Standard message reading loop</span></pre>
<pre class="cl"><span class="cb3">while</span> ((inc = Client.ReadMessage()) != <span class="cb1">null</span>)</pre>
<pre class="cl">{</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; <span class="cb3">switch</span> (inc.MessageType)</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; {</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DiscoveryResponse:</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb2">Console</span>.WriteLine(<span class="cb4">&quot;Found server at &quot;</span> + inc.SenderEndpoint + <span class="cb4">&quot; name: &quot;</span> + inc.ReadString());</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">break</span>;</pre>
</div>
</td>
</tr>
</table>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<style type="text/css">
body
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
margin-left: 20px;
background-color: #dddddd;
}
td
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
}
.page
{
width: 700px;
}
.cf
{
font-family: Courier New;
font-size: 10pt;
color: black;
background: white;
padding: 16px;
border: 1px solid black;
}
.cl
{
margin: 0px;
}
.cb1
{
color: green;
}
.cb2
{
color: #2b91af;
}
.cb3
{
color: blue;
}
.cb4
{
color: #a31515;
}
</style>
<title>Peer/server discovery</title>
</head>
<body>
<table>
<tr>
<td class="page">
<h1>Peer/server discovery</h1>
<p>
Peer discovery is the process of clients detecting what servers are available. Discovery requests can be made in two ways;
locally as a broadcast, which will send a signal to all peers on your subnet. Secondly you can contact an ip address directly
and query it if a server is running.
</p>
<p>Responding to discovery requests are done in the same way regardless of how the request is made.</p>
<p>Here's how to do on the client side; ie. the side which makes a request:</p>
<div class="cf">
<pre class="cl"><span class="cb1">// Enable DiscoveryResponse messages</span></pre>
<pre class="cl">config.EnableMessageType(<span class="cb2">NetIncomingMessageType</span>.DiscoveryResponse);</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl"><span class="cb1">// Emit a discovery signal</span></pre>
<pre class="cl">Client.DiscoverLocalPeers(14242);</pre>
</div>
<p>This will send a discovery signal to your subnet; Here's how to receive the signal on the server side, and send a response back to the client:</p>
<div class="cf">
<pre class="cl"><span class="cb1">// Enable DiscoveryRequest messages</span></pre>
<pre class="cl">config.EnableMessageType(<span class="cb2">NetIncomingMessageType</span>.DiscoveryRequest);</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl"><span class="cb1">// Standard message reading loop</span></pre>
<pre class="cl"><span class="cb3">while</span> ((inc = Server.ReadMessage()) != <span class="cb1">null</span>)</pre>
<pre class="cl">{</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; <span class="cb3">switch</span> (inc.MessageType)</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; {</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DiscoveryRequest:</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb1">// Create a response and write some example data to it</span></pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb2">NetOutgoingMessage</span> response = Server.CreateMessage();</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; response.Write(<span class="cb4">&quot;My server name&quot;</span>);</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb1">// Send the response to the sender of the request</span></pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Server.SendDiscoveryResponse(response, inc.SenderEndpoint);</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">break</span>;</pre>
</div>
<p>When the response then reaches the client, you can read the data you wrote on the server:</p>
<div class="cf">
<pre class="cl"><span class="cb1">// Standard message reading loop</span></pre>
<pre class="cl"><span class="cb3">while</span> ((inc = Client.ReadMessage()) != <span class="cb1">null</span>)</pre>
<pre class="cl">{</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; <span class="cb3">switch</span> (inc.MessageType)</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; {</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DiscoveryResponse:</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb2">Console</span>.WriteLine(<span class="cb4">&quot;Found server at &quot;</span> + inc.SenderEndpoint + <span class="cb4">&quot; name: &quot;</span> + inc.ReadString());</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">break</span>;</pre>
</div>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,22 +1,22 @@
Improvements over last version of library:
* New delivery type: Reliable sequenced (Lost packets are resent but late arrivals are dropped)
* Disconnects and shutdown requests are now queued properly, so calling shutdown will still send any queued messages before shutting down
* All messages are pooled/recycled for zero garbage
* Reduced CPU usage and lower latencies (in the <1 ms range, but still) due to better socket polling
* All public members of NetPeer/NetConnection are completely thread safe
* Larger number of delivery channels
* More exact roundtrip measurement
* Method serialize entire objects via reflection
* Unique identifier now exists for all peers/connections
* More flexible peer discovery; filters possible and arbitrary data can be sent with response
* Much better protection against malformed messages crashing the app
API enhancements:
* NetPeerConfiguration immutable properties now locked once NetPeer is initialized
* Messages cannot be send twice by accident
* Impossible to confuse sending and receiving buffers since they're different classes
* No more confusion if user should create a buffer or preallocate and reuse
Improvements over last version of library:
* New delivery type: Reliable sequenced (Lost packets are resent but late arrivals are dropped)
* Disconnects and shutdown requests are now queued properly, so calling shutdown will still send any queued messages before shutting down
* All messages are pooled/recycled for zero garbage
* Reduced CPU usage and lower latencies (in the <1 ms range, but still) due to better socket polling
* All public members of NetPeer/NetConnection are completely thread safe
* Larger number of delivery channels
* More exact roundtrip measurement
* Method serialize entire objects via reflection
* Unique identifier now exists for all peers/connections
* More flexible peer discovery; filters possible and arbitrary data can be sent with response
* Much better protection against malformed messages crashing the app
API enhancements:
* NetPeerConfiguration immutable properties now locked once NetPeer is initialized
* Messages cannot be send twice by accident
* Impossible to confuse sending and receiving buffers since they're different classes
* No more confusion if user should create a buffer or preallocate and reuse

View File

@@ -1,17 +1,17 @@
PER MESSAGE:
7 bits - NetMessageType
1 bit - Is a message fragment?
[8 bits NetMessageLibraryType, if NetMessageType == Library]
[16 bits sequence number, if NetMessageType >= UserSequenced]
8/16 bits - Payload length in bits (variable size ushort)
[16 bits fragments group id, if fragmented]
[16 bits fragments total count, if fragmented]
[16 bits fragment number, if fragmented]
[x - Payload] if length > 0
PER MESSAGE:
7 bits - NetMessageType
1 bit - Is a message fragment?
[8 bits NetMessageLibraryType, if NetMessageType == Library]
[16 bits sequence number, if NetMessageType >= UserSequenced]
8/16 bits - Payload length in bits (variable size ushort)
[16 bits fragments group id, if fragmented]
[16 bits fragments total count, if fragmented]
[16 bits fragment number, if fragmented]
[x - Payload] if length > 0

View File

@@ -1,115 +1,115 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<style type="text/css">
body
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
margin-left: 20px;
background-color: #dddddd;
}
td
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
}
.cf
{
font-family: Courier New;
font-size: 10pt;
color: black;
background: white;
padding: 16px;
border: 1px solid black;
}
.cl
{
margin: 0px;
}
.cb1
{
color: green;
}
.cb2
{
color: #2b91af;
}
.cb3
{
color: blue;
}
.cb4
{
color: #a31515;
}
</style>
<title>Lidgren tutorial</title>
</head>
<body>
<table>
<tr>
<td width="700">
<h1>Simulating bad network conditions</h1>
<p>
On the internet, your packets are likely to run in to all kinds of trouble. They will be delayed and lost and they might even arrive multiple times at the destination. Lidgren has a few option to simulate how your application or game will react when this happens.<br />
They are all configured using the NetPeerConfiguration class - these properties exists:</p>
<p>
<div class="cf">
<table>
<tr>
<td valign="top">
<b>SimulatedLoss</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This is a float which simulates lost packets. A value of 0 will disable this feature, a value of 0.5f will make half of your sent packets disappear, chosen randomly. Note that packets may contain several messages - this is the amount of packets lost.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>SimulatedDuplicatesChance</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This is a float which determines the chance that a packet will be duplicated at the destination. 0 means no packets will be duplicated, 0.5f means that on average, every other packet will be duplicated.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>SimulatedMinimumLatency</b><br />
<b>SimulatedRandomLatency</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
These two properties control simulating delay of packets in seconds (not milliseconds, use 0.05 for 50 ms of lag). They work on top of the actual network delay and the total delay will be:<br />
Actual one way latency + SimulatedMinimumLatency + [Randomly per packet 0 to SimulatedRandomLatency seconds]
</td>
</tr>
</table>
</div>
<p>It's recommended to assume symmetric condtions and configure server and client with the same simulation settings.</p>
<p>Simulating bad network conditions only works in DEBUG builds.</p>
</td>
</tr>
</table>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<style type="text/css">
body
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
margin-left: 20px;
background-color: #dddddd;
}
td
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
}
.cf
{
font-family: Courier New;
font-size: 10pt;
color: black;
background: white;
padding: 16px;
border: 1px solid black;
}
.cl
{
margin: 0px;
}
.cb1
{
color: green;
}
.cb2
{
color: #2b91af;
}
.cb3
{
color: blue;
}
.cb4
{
color: #a31515;
}
</style>
<title>Lidgren tutorial</title>
</head>
<body>
<table>
<tr>
<td width="700">
<h1>Simulating bad network conditions</h1>
<p>
On the internet, your packets are likely to run in to all kinds of trouble. They will be delayed and lost and they might even arrive multiple times at the destination. Lidgren has a few option to simulate how your application or game will react when this happens.<br />
They are all configured using the NetPeerConfiguration class - these properties exists:</p>
<p>
<div class="cf">
<table>
<tr>
<td valign="top">
<b>SimulatedLoss</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This is a float which simulates lost packets. A value of 0 will disable this feature, a value of 0.5f will make half of your sent packets disappear, chosen randomly. Note that packets may contain several messages - this is the amount of packets lost.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>SimulatedDuplicatesChance</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This is a float which determines the chance that a packet will be duplicated at the destination. 0 means no packets will be duplicated, 0.5f means that on average, every other packet will be duplicated.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>SimulatedMinimumLatency</b><br />
<b>SimulatedRandomLatency</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
These two properties control simulating delay of packets in seconds (not milliseconds, use 0.05 for 50 ms of lag). They work on top of the actual network delay and the total delay will be:<br />
Actual one way latency + SimulatedMinimumLatency + [Randomly per packet 0 to SimulatedRandomLatency seconds]
</td>
</tr>
</table>
</div>
<p>It's recommended to assume symmetric condtions and configure server and client with the same simulation settings.</p>
<p>Simulating bad network conditions only works in DEBUG builds.</p>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,17 +1,17 @@
Completed features:
* Message coalescing
* Peer, connection statistics
* Lag, loss and duplication simulation for testing
* Connection approval
* Throttling
* Clock synchronization to detect jitter per packet (NetTime.RemoteNow)
* Peer discovery
* Message fragmentation
Missing features:
* Receipts 25% done, need design
* More realistic lag/loss (lumpy)
* Detect estimated packet loss
* More advanced ack packet
Completed features:
* Message coalescing
* Peer, connection statistics
* Lag, loss and duplication simulation for testing
* Connection approval
* Throttling
* Clock synchronization to detect jitter per packet (NetTime.RemoteNow)
* Peer discovery
* Message fragmentation
Missing features:
* Receipts 25% done, need design
* More realistic lag/loss (lumpy)
* Detect estimated packet loss
* More advanced ack packet

View File

@@ -1,206 +1,206 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<style type="text/css">
body
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
margin-left: 20px;
background-color: #dddddd;
}
td
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
}
.cf
{
font-family: Courier New;
font-size: 10pt;
color: black;
background: white;
padding: 16px;
border: 1px solid black;
}
.cl
{
margin: 0px;
}
.cb1
{
color: green;
}
.cb2
{
color: #2b91af;
}
.cb3
{
color: blue;
}
.cb4
{
color: #a31515;
}
</style>
<title>Lidgren basics tutorial</title>
</head>
<body>
<table>
<tr>
<td width="700">
<h1>
Lidgren basics</h1>
<p>
Lidgren network library is all about messages. There are two types of messages:</p>
<li>Library messages telling you things like a peer has connected or diagnostics messages (warnings, errors) when unexpected things happen.</li>
<li>Data messages which is data sent from a remote (connected or unconnected) peer.</li>
<p>
The base class for establishing connections, receiving and sending message are the NetPeer class. Using it you can make a peer-to-peer network, but if you are creating a server/client topology there are special classes called NetServer and NetClient. They inherit NetPeer but sets some defaults and includes some helper methods/properties.</p>
<p>
Here's how to set up a NetServer:</p>
<div class="cf">
<pre class="cl"><span class="cb2">NetPeerConfiguration</span> config = <span class="cb3">new</span> <span class="cb2">NetPeerConfiguration</span>(<span class="cb4">&quot;MyExampleName&quot;</span>);</pre>
<pre class="cl">config.Port = 14242;</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl"><span class="cb2">NetServer</span> server = <span class="cb3">new</span> <span class="cb2">NetServer</span>(config);</pre>
<pre class="cl">server.Start();</pre>
</div>
<p>
The code above first creates a configuration. It has lots of properties you can change, but the default values should be pretty good for most applications. The string you provide in the constructor (MyExampleName) is an identifier to distinquish it from other applications using the lidgren library. Just make sure you use the same string in both server and client - or you will be unable to communicate between them.</p>
<p>
Secondly we've set the local port the server should listen to. This is the port number we tell the client(s) what port number to connect to. The local port can be set for a client too, but it's not needed and not recommended.</p>
<p>
Thirdly we create our server object and fourth we Start() it. Starting the server will create a new network thread and bind to a socket and start listening for connections.</p>
<p>
Early on we spoke about messages; now is the time to start receiving and sending some. Here's a code snippet for receiving messages:</p>
<div class="cf">
<pre class="cl"><span class="cb2">NetIncomingMessage</span> msg;</pre>
<pre class="cl"><span class="cb3">while</span> ((msg = server.ReadMessage()) != <span class="cb3">null</span>)</pre>
<pre class="cl">{</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; <span class="cb3">switch</span> (msg.MessageType)</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; {</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.VerboseDebugMessage:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DebugMessage:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.WarningMessage:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.ErrorMessage:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb2">Console</span>.WriteLine(msg.ReadString());</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">break</span>;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">default</span>:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb2">Console</span>.WriteLine(<span class="cb4">&quot;Unhandled type: &quot;</span> + msg.MessageType);</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">break</span>;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; }</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; server.Recycle(msg);</pre>
<pre class="cl">}</pre>
</div>
<p>
So, lets dissect the above code. First we declare a NetIncomingMessage, which is the type of incoming messages. Then we read a message and handles it, looping back as long as there are messages to fetch. For each message we find, we switch on sometime called MessageType - it's a description what the message contains. In this code example we only catch messages of type VerboseDebugMessage, DebugMessage, WarningMessage and ErrorMessage. All those four types are emitted by the library to inform about various events. They all contains a single string, so we use the method ReadString() to extract a copy of that string and print it in the console.</p>
<p>
Reading data will increment the internal message pointer so you can read subsequent data using the Read*() methods.</p>
<p>
For all other message type we just print that it's currently unhandled.</p>
<p>
Finally, we recycle the message after we're done with it - this will enable the library to reuse the object and create less garbage.</p>
<p>
Sending messages are even easier:</p>
<div class="cf">
<pre class="cl"><span class="cb2">NetOutgoingMessage</span> sendMsg = server.CreateMessage();</pre>
<pre class="cl">sendMsg.Write(<span class="cb4">&quot;Hello&quot;</span>);</pre>
<pre class="cl">sendMsg.Write(42);</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl">server.SendMessage(sendMsg, recipient, <span class="cb2">NetDeliveryMethod</span>.ReliableOrdered);</pre>
</div>
<p>
The above code first creates a new message, or uses a recycled message, which is why it's not possible to just create a message using new(). It then writes a string ("Hello") and an integer (System.Int32, 4 bytes in size) to the message.</p>
<p>
Then the message is sent using the SendMessage() method. The first argument is the message to send, the second argument is the recipient connection - which we'll not go into detail about just yet - and the third argument are HOW to deliver the message, or rather how to behave if network conditions are bad and a packet gets lost, duplicated or reordered.</p>
<p>
There are five delivery methods available:</p>
<div class="cf">
<table>
<tr>
<td valign="top">
<b>Unreliable</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This is just UDP. Messages can be lost, received more than once and messages sent after other messages may be received before them.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>UnreliableSequenced</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
Using this delivery method messages can still be lost; but you're protected against duplicated messages and if a message arrives late; that is, if a message sent after this one has already been received - it will be dropped. This means you will never receive "older" data than what you already have received.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>ReliableUnordered</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This delivery method ensures that every message sent will be received eventually. It does not however guarantee what order they will be received; late messages may be delivered before older ones.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>ReliableSequenced</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This delivery method is similar to UnreliableSequenced; except that is guarantees that SOME messages will be received - if you only send one message - it will be received. If you sent two messages quickly, and they get reordered in transit, only the newest message will be received - but at least ONE of them will be received guaranteed.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top"><b>ReliableOrdered</b></td>
<td>&nbsp;</td>
<td valign="top">
This delivery method guarantees that messages will always be received in the exact order they were sent.
</td>
</tr>
</table>
</div>
<p>
Here's how to read and decode the message above:</p>
<div class="cf">
<pre class="cl"><span class="cb2">NetIncomingMessage</span> incMsg = server.ReadMessage();</pre>
<pre class="cl"><span class="cb3">string</span> str = incMsg.ReadString();</pre>
<pre class="cl"><span class="cb3">int</span> a = incMsg.ReadInt32();</pre>
</div>
</td>
</tr>
</table>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<style type="text/css">
body
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
margin-left: 20px;
background-color: #dddddd;
}
td
{
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: small;
}
.cf
{
font-family: Courier New;
font-size: 10pt;
color: black;
background: white;
padding: 16px;
border: 1px solid black;
}
.cl
{
margin: 0px;
}
.cb1
{
color: green;
}
.cb2
{
color: #2b91af;
}
.cb3
{
color: blue;
}
.cb4
{
color: #a31515;
}
</style>
<title>Lidgren basics tutorial</title>
</head>
<body>
<table>
<tr>
<td width="700">
<h1>
Lidgren basics</h1>
<p>
Lidgren network library is all about messages. There are two types of messages:</p>
<li>Library messages telling you things like a peer has connected or diagnostics messages (warnings, errors) when unexpected things happen.</li>
<li>Data messages which is data sent from a remote (connected or unconnected) peer.</li>
<p>
The base class for establishing connections, receiving and sending message are the NetPeer class. Using it you can make a peer-to-peer network, but if you are creating a server/client topology there are special classes called NetServer and NetClient. They inherit NetPeer but sets some defaults and includes some helper methods/properties.</p>
<p>
Here's how to set up a NetServer:</p>
<div class="cf">
<pre class="cl"><span class="cb2">NetPeerConfiguration</span> config = <span class="cb3">new</span> <span class="cb2">NetPeerConfiguration</span>(<span class="cb4">&quot;MyExampleName&quot;</span>);</pre>
<pre class="cl">config.Port = 14242;</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl"><span class="cb2">NetServer</span> server = <span class="cb3">new</span> <span class="cb2">NetServer</span>(config);</pre>
<pre class="cl">server.Start();</pre>
</div>
<p>
The code above first creates a configuration. It has lots of properties you can change, but the default values should be pretty good for most applications. The string you provide in the constructor (MyExampleName) is an identifier to distinquish it from other applications using the lidgren library. Just make sure you use the same string in both server and client - or you will be unable to communicate between them.</p>
<p>
Secondly we've set the local port the server should listen to. This is the port number we tell the client(s) what port number to connect to. The local port can be set for a client too, but it's not needed and not recommended.</p>
<p>
Thirdly we create our server object and fourth we Start() it. Starting the server will create a new network thread and bind to a socket and start listening for connections.</p>
<p>
Early on we spoke about messages; now is the time to start receiving and sending some. Here's a code snippet for receiving messages:</p>
<div class="cf">
<pre class="cl"><span class="cb2">NetIncomingMessage</span> msg;</pre>
<pre class="cl"><span class="cb3">while</span> ((msg = server.ReadMessage()) != <span class="cb3">null</span>)</pre>
<pre class="cl">{</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; <span class="cb3">switch</span> (msg.MessageType)</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; {</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.VerboseDebugMessage:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DebugMessage:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.WarningMessage:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.ErrorMessage:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb2">Console</span>.WriteLine(msg.ReadString());</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">break</span>;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">default</span>:</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb2">Console</span>.WriteLine(<span class="cb4">&quot;Unhandled type: &quot;</span> + msg.MessageType);</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span class="cb3">break</span>;</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; }</pre>
<pre class="cl">&nbsp;&nbsp;&nbsp; server.Recycle(msg);</pre>
<pre class="cl">}</pre>
</div>
<p>
So, lets dissect the above code. First we declare a NetIncomingMessage, which is the type of incoming messages. Then we read a message and handles it, looping back as long as there are messages to fetch. For each message we find, we switch on sometime called MessageType - it's a description what the message contains. In this code example we only catch messages of type VerboseDebugMessage, DebugMessage, WarningMessage and ErrorMessage. All those four types are emitted by the library to inform about various events. They all contains a single string, so we use the method ReadString() to extract a copy of that string and print it in the console.</p>
<p>
Reading data will increment the internal message pointer so you can read subsequent data using the Read*() methods.</p>
<p>
For all other message type we just print that it's currently unhandled.</p>
<p>
Finally, we recycle the message after we're done with it - this will enable the library to reuse the object and create less garbage.</p>
<p>
Sending messages are even easier:</p>
<div class="cf">
<pre class="cl"><span class="cb2">NetOutgoingMessage</span> sendMsg = server.CreateMessage();</pre>
<pre class="cl">sendMsg.Write(<span class="cb4">&quot;Hello&quot;</span>);</pre>
<pre class="cl">sendMsg.Write(42);</pre>
<pre class="cl">&nbsp;</pre>
<pre class="cl">server.SendMessage(sendMsg, recipient, <span class="cb2">NetDeliveryMethod</span>.ReliableOrdered);</pre>
</div>
<p>
The above code first creates a new message, or uses a recycled message, which is why it's not possible to just create a message using new(). It then writes a string ("Hello") and an integer (System.Int32, 4 bytes in size) to the message.</p>
<p>
Then the message is sent using the SendMessage() method. The first argument is the message to send, the second argument is the recipient connection - which we'll not go into detail about just yet - and the third argument are HOW to deliver the message, or rather how to behave if network conditions are bad and a packet gets lost, duplicated or reordered.</p>
<p>
There are five delivery methods available:</p>
<div class="cf">
<table>
<tr>
<td valign="top">
<b>Unreliable</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This is just UDP. Messages can be lost, received more than once and messages sent after other messages may be received before them.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>UnreliableSequenced</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
Using this delivery method messages can still be lost; but you're protected against duplicated messages and if a message arrives late; that is, if a message sent after this one has already been received - it will be dropped. This means you will never receive "older" data than what you already have received.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>ReliableUnordered</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This delivery method ensures that every message sent will be received eventually. It does not however guarantee what order they will be received; late messages may be delivered before older ones.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top">
<b>ReliableSequenced</b>
</td>
<td>
&nbsp;
</td>
<td valign="top">
This delivery method is similar to UnreliableSequenced; except that is guarantees that SOME messages will be received - if you only send one message - it will be received. If you sent two messages quickly, and they get reordered in transit, only the newest message will be received - but at least ONE of them will be received guaranteed.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td valign="top"><b>ReliableOrdered</b></td>
<td>&nbsp;</td>
<td valign="top">
This delivery method guarantees that messages will always be received in the exact order they were sent.
</td>
</tr>
</table>
</div>
<p>
Here's how to read and decode the message above:</p>
<div class="cf">
<pre class="cl"><span class="cb2">NetIncomingMessage</span> incMsg = server.ReadMessage();</pre>
<pre class="cl"><span class="cb3">string</span> str = incMsg.ReadString();</pre>
<pre class="cl"><span class="cb3">int</span> a = incMsg.ReadInt32();</pre>
</div>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,21 +1,21 @@
using System;
using System.Collections.Generic;
namespace Lidgren.Network
{
/// <summary>
/// Interface for an encryption algorithm
/// </summary>
public interface INetEncryption
{
/// <summary>
/// Encrypt an outgoing message in place
/// </summary>
bool Encrypt(NetOutgoingMessage msg);
/// <summary>
/// Decrypt an incoming message in place
/// </summary>
bool Decrypt(NetIncomingMessage msg);
}
}
using System;
using System.Collections.Generic;
namespace Lidgren.Network
{
/// <summary>
/// Interface for an encryption algorithm
/// </summary>
public interface INetEncryption
{
/// <summary>
/// Encrypt an outgoing message in place
/// </summary>
bool Encrypt(NetOutgoingMessage msg);
/// <summary>
/// Decrypt an incoming message in place
/// </summary>
bool Decrypt(NetIncomingMessage msg);
}
}

View File

@@ -1,163 +1,163 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// AES encryption
/// </summary>
public class NetAESEncryption : INetEncryption
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetAESEncryption()
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in aes.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in aes.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(string key)
: this(key, m_keysizes[0])
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{
try
{
// nested usings are fun!
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{
try
{
// nested usings are fun!
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// AES encryption
/// </summary>
public class NetAESEncryption : INetEncryption
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetAESEncryption()
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in aes.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in aes.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(string key)
: this(key, m_keysizes[0])
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{
try
{
// nested usings are fun!
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{
try
{
// nested usings are fun!
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
}
}

View File

@@ -1,88 +1,88 @@
using System;
using System.Collections.Generic;
namespace Lidgren.Network
{
/// <summary>
/// Base for a non-threadsafe encryption class
/// </summary>
public abstract class NetBlockEncryptionBase : INetEncryption
{
// temporary space for one block to avoid reallocating every time
private byte[] m_tmp;
/// <summary>
/// Block size in bytes for this cipher
/// </summary>
public abstract int BlockSize { get; }
/// <summary>
/// NetBlockEncryptionBase constructor
/// </summary>
public NetBlockEncryptionBase()
{
m_tmp = new byte[BlockSize];
}
/// <summary>
/// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{
int payloadBitLength = msg.LengthBits;
int numBytes = msg.LengthBytes;
int blockSize = BlockSize;
int numBlocks = (int)Math.Ceiling((double)numBytes / (double)blockSize);
int dstSize = numBlocks * blockSize;
msg.EnsureBufferSize(dstSize * 8 + (4 * 8)); // add 4 bytes for payload length at end
msg.LengthBits = dstSize * 8; // length will automatically adjust +4 bytes when payload length is written
for(int i=0;i<numBlocks;i++)
{
EncryptBlock(msg.m_data, (i * blockSize), m_tmp);
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
}
// add true payload length last
msg.Write((UInt32)payloadBitLength);
return true;
}
/// <summary>
/// Decrypt an incoming message encrypted with corresponding Encrypt
/// </summary>
/// <param name="msg">message to decrypt</param>
/// <returns>true if successful; false if failed</returns>
public bool Decrypt(NetIncomingMessage msg)
{
int numEncryptedBytes = msg.LengthBytes - 4; // last 4 bytes is true bit length
int blockSize = BlockSize;
int numBlocks = numEncryptedBytes / blockSize;
if (numBlocks * blockSize != numEncryptedBytes)
return false;
for (int i = 0; i < numBlocks; i++)
{
DecryptBlock(msg.m_data, (i * blockSize), m_tmp);
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
}
// read 32 bits of true payload length
uint realSize = NetBitWriter.ReadUInt32(msg.m_data, 32, (numEncryptedBytes * 8));
msg.m_bitLength = (int)realSize;
return true;
}
/// <summary>
/// Encrypt a block of bytes
/// </summary>
protected abstract void EncryptBlock(byte[] source, int sourceOffset, byte[] destination);
/// <summary>
/// Decrypt a block of bytes
/// </summary>
protected abstract void DecryptBlock(byte[] source, int sourceOffset, byte[] destination);
}
}
using System;
using System.Collections.Generic;
namespace Lidgren.Network
{
/// <summary>
/// Base for a non-threadsafe encryption class
/// </summary>
public abstract class NetBlockEncryptionBase : INetEncryption
{
// temporary space for one block to avoid reallocating every time
private byte[] m_tmp;
/// <summary>
/// Block size in bytes for this cipher
/// </summary>
public abstract int BlockSize { get; }
/// <summary>
/// NetBlockEncryptionBase constructor
/// </summary>
public NetBlockEncryptionBase()
{
m_tmp = new byte[BlockSize];
}
/// <summary>
/// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{
int payloadBitLength = msg.LengthBits;
int numBytes = msg.LengthBytes;
int blockSize = BlockSize;
int numBlocks = (int)Math.Ceiling((double)numBytes / (double)blockSize);
int dstSize = numBlocks * blockSize;
msg.EnsureBufferSize(dstSize * 8 + (4 * 8)); // add 4 bytes for payload length at end
msg.LengthBits = dstSize * 8; // length will automatically adjust +4 bytes when payload length is written
for(int i=0;i<numBlocks;i++)
{
EncryptBlock(msg.m_data, (i * blockSize), m_tmp);
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
}
// add true payload length last
msg.Write((UInt32)payloadBitLength);
return true;
}
/// <summary>
/// Decrypt an incoming message encrypted with corresponding Encrypt
/// </summary>
/// <param name="msg">message to decrypt</param>
/// <returns>true if successful; false if failed</returns>
public bool Decrypt(NetIncomingMessage msg)
{
int numEncryptedBytes = msg.LengthBytes - 4; // last 4 bytes is true bit length
int blockSize = BlockSize;
int numBlocks = numEncryptedBytes / blockSize;
if (numBlocks * blockSize != numEncryptedBytes)
return false;
for (int i = 0; i < numBlocks; i++)
{
DecryptBlock(msg.m_data, (i * blockSize), m_tmp);
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
}
// read 32 bits of true payload length
uint realSize = NetBitWriter.ReadUInt32(msg.m_data, 32, (numEncryptedBytes * 8));
msg.m_bitLength = (int)realSize;
return true;
}
/// <summary>
/// Encrypt a block of bytes
/// </summary>
protected abstract void EncryptBlock(byte[] source, int sourceOffset, byte[] destination);
/// <summary>
/// Decrypt a block of bytes
/// </summary>
protected abstract void DecryptBlock(byte[] source, int sourceOffset, byte[] destination);
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace Lidgren.Network
{
public abstract class NetCryptoProviderBase : NetEncryption
{
protected SymmetricAlgorithm m_algorithm;
public NetCryptoProviderBase(NetPeer peer, SymmetricAlgorithm algo)
: base(peer)
{
m_algorithm = algo;
m_algorithm.GenerateKey();
m_algorithm.GenerateIV();
}
public override void SetKey(byte[] data, int offset, int count)
{
int len = m_algorithm.Key.Length;
var key = new byte[len];
for (int i = 0; i < len; i++)
key[i] = data[offset + (i % count)];
m_algorithm.Key = key;
len = m_algorithm.IV.Length;
key = new byte[len];
for (int i = 0; i < len; i++)
key[len - 1 - i] = data[offset + (i % count)];
m_algorithm.IV = key;
}
public override bool Encrypt(NetOutgoingMessage msg)
{
int unEncLenBits = msg.LengthBits;
var ms = new MemoryStream();
var cs = new CryptoStream(ms, m_algorithm.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(msg.m_data, 0, msg.LengthBytes);
cs.Close();
// get results
var arr = ms.ToArray();
ms.Close();
msg.EnsureBufferSize((arr.Length + 4) * 8);
msg.LengthBits = 0; // reset write pointer
msg.Write((uint)unEncLenBits);
msg.Write(arr);
msg.LengthBits = (arr.Length + 4) * 8;
return true;
}
public override bool Decrypt(NetIncomingMessage msg)
{
int unEncLenBits = (int)msg.ReadUInt32();
var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4);
var cs = new CryptoStream(ms, m_algorithm.CreateDecryptor(), CryptoStreamMode.Read);
var byteLen = NetUtility.BytesToHoldBits(unEncLenBits);
var result = m_peer.GetStorage(byteLen);
cs.Read(result, 0, byteLen);
cs.Close();
// TODO: recycle existing msg
msg.m_data = result;
msg.m_bitLength = unEncLenBits;
msg.m_readPosition = 0;
return true;
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace Lidgren.Network
{
public abstract class NetCryptoProviderEncryption : NetEncryption
{
public NetCryptoProviderEncryption(NetPeer peer)
: base(peer)
{
}
protected abstract CryptoStream GetEncryptStream(MemoryStream ms);
protected abstract CryptoStream GetDecryptStream(MemoryStream ms);
public override bool Encrypt(NetOutgoingMessage msg)
{
int unEncLenBits = msg.LengthBits;
var ms = new MemoryStream();
var cs = GetEncryptStream(ms);
cs.Write(msg.m_data, 0, msg.LengthBytes);
cs.Close();
// get results
var arr = ms.ToArray();
ms.Close();
msg.EnsureBufferSize((arr.Length + 4) * 8);
msg.LengthBits = 0; // reset write pointer
msg.Write((uint)unEncLenBits);
msg.Write(arr);
msg.LengthBits = (arr.Length + 4) * 8;
return true;
}
public override bool Decrypt(NetIncomingMessage msg)
{
int unEncLenBits = (int)msg.ReadUInt32();
var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4);
var cs = GetDecryptStream(ms);
var result = m_peer.GetStorage(unEncLenBits);
cs.Read(result, 0, NetUtility.BytesToHoldBits(unEncLenBits));
cs.Close();
// TODO: recycle existing msg
msg.m_data = result;
msg.m_bitLength = unEncLenBits;
return true;
}
}
}

View File

@@ -1,164 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// DES encryption
/// </summary>
public class NetDESEncryption : INetEncryption
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetDESEncryption()
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in des.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in des.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
}
/// <summary>
/// NetDESEncryption constructor
/// </summary>
public NetDESEncryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetDESEncryption constructor
/// </summary>
public NetDESEncryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetDESEncryption constructor
/// </summary>
public NetDESEncryption(string key)
: this(key, m_keysizes[0])
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{
try
{
// nested usings are fun!
using (DESCryptoServiceProvider desCryptoServiceProvider = new DESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = desCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{
try
{
// nested usings are fun!
using (DESCryptoServiceProvider desCryptoServiceProvider = new DESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = desCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace Lidgren.Network
{
/// <summary>
/// Interface for an encryption algorithm
/// </summary>
public abstract class NetEncryption
{
/// <summary>
/// NetPeer
/// </summary>
protected NetPeer m_peer;
/// <summary>
/// Constructor
/// </summary>
public NetEncryption(NetPeer peer)
{
if (peer == null)
throw new NetException("Peer must not be null");
m_peer = peer;
}
public void SetKey(string str)
{
var bytes = System.Text.Encoding.ASCII.GetBytes(str);
SetKey(bytes, 0, bytes.Length);
}
public abstract void SetKey(byte[] data, int offset, int count);
/// <summary>
/// Encrypt an outgoing message in place
/// </summary>
public abstract bool Encrypt(NetOutgoingMessage msg);
/// <summary>
/// Decrypt an incoming message in place
/// </summary>
public abstract bool Decrypt(NetIncomingMessage msg);
}
}

View File

@@ -1,165 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// RC2 encryption
/// </summary>
public class NetRC2Encryption : INetEncryption
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetRC2Encryption()
{
RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in rc2.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in rc2.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
}
/// <summary>
/// NetRC2Encryption constructor
/// </summary>
public NetRC2Encryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetRC2Encryption constructor
/// </summary>
public NetRC2Encryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetRC2Encryption constructor
/// </summary>
/// <param name="key"></param>
public NetRC2Encryption(string key)
: this(key, m_keysizes[0])
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{
try
{
// nested usings are fun!
using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{
try
{
// nested usings are fun!
using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
}
}

View File

@@ -1,164 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Triple DES encryption
/// </summary>
public class NetTripleDESEncryption : INetEncryption
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetTripleDESEncryption()
{
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in tripleDES.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in tripleDES.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
}
/// <summary>
/// NetTriplsDESEncryption constructor
/// </summary>
public NetTripleDESEncryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetTriplsDESEncryption constructor
/// </summary>
public NetTripleDESEncryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetTriplsDESEncryption constructor
/// </summary>
public NetTripleDESEncryption(string key)
: this(key, m_keysizes[0])
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{
try
{
// nested usings are fun!
using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{
try
{
// nested usings are fun!
using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
}
}
}

View File

@@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Example class; not very good encryption
/// </summary>
public class NetXorEncryption : INetEncryption
{
private byte[] m_key;
/// <summary>
/// NetXorEncryption constructor
/// </summary>
public NetXorEncryption(byte[] key)
{
m_key = key;
}
/// <summary>
/// NetXorEncryption constructor
/// </summary>
public NetXorEncryption(string key)
{
m_key = Encoding.UTF8.GetBytes(key);
}
/// <summary>
/// Encrypt an outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{
int numBytes = msg.LengthBytes;
for (int i = 0; i < numBytes; i++)
{
int offset = i % m_key.Length;
msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
}
return true;
}
/// <summary>
/// Decrypt an incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{
int numBytes = msg.LengthBytes;
for (int i = 0; i < numBytes; i++)
{
int offset = i % m_key.Length;
msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
}
return true;
}
}
}

View File

@@ -1,146 +1,146 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Security.Cryptography;
using System.Text;
using System.Security;
namespace Lidgren.Network
{
/// <summary>
/// Methods to encrypt and decrypt data using the XTEA algorithm
/// </summary>
public sealed class NetXtea : NetBlockEncryptionBase
{
private const int c_blockSize = 8;
private const int c_keySize = 16;
private const int c_delta = unchecked((int)0x9E3779B9);
private readonly int m_numRounds;
private readonly uint[] m_sum0;
private readonly uint[] m_sum1;
/// <summary>
/// Gets the block size for this cipher
/// </summary>
public override int BlockSize { get { return c_blockSize; } }
/// <summary>
/// 16 byte key
/// </summary>
public NetXtea(byte[] key, int rounds)
{
if (key.Length < c_keySize)
throw new NetException("Key too short!");
m_numRounds = rounds;
m_sum0 = new uint[m_numRounds];
m_sum1 = new uint[m_numRounds];
uint[] tmp = new uint[8];
int num2;
int index = num2 = 0;
while (index < 4)
{
tmp[index] = BitConverter.ToUInt32(key, num2);
index++;
num2 += 4;
}
for (index = num2 = 0; index < 32; index++)
{
m_sum0[index] = ((uint)num2) + tmp[num2 & 3];
num2 += -1640531527;
m_sum1[index] = ((uint)num2) + tmp[(num2 >> 11) & 3];
}
}
/// <summary>
/// 16 byte key
/// </summary>
public NetXtea(byte[] key)
: this(key, 32)
{
}
/// <summary>
/// String to hash for key
/// </summary>
public NetXtea(string key)
: this(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key)), 32)
{
}
/// <summary>
/// Encrypts a block of bytes
/// </summary>
protected override void EncryptBlock(byte[] source, int sourceOffset, byte[] destination)
{
uint v0 = BytesToUInt(source, sourceOffset);
uint v1 = BytesToUInt(source, sourceOffset + 4);
for (int i = 0; i != m_numRounds; i++)
{
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
}
UIntToBytes(v0, destination, 0);
UIntToBytes(v1, destination, 0 + 4);
return;
}
/// <summary>
/// Decrypts a block of bytes
/// </summary>
protected override void DecryptBlock(byte[] source, int sourceOffset, byte[] destination)
{
// Pack bytes into integers
uint v0 = BytesToUInt(source, sourceOffset);
uint v1 = BytesToUInt(source, sourceOffset + 4);
for (int i = m_numRounds - 1; i >= 0; i--)
{
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
}
UIntToBytes(v0, destination, 0);
UIntToBytes(v1, destination, 0 + 4);
return;
}
private static uint BytesToUInt(byte[] bytes, int offset)
{
uint retval = (uint)(bytes[offset] << 24);
retval |= (uint)(bytes[++offset] << 16);
retval |= (uint)(bytes[++offset] << 8);
return (retval | bytes[++offset]);
}
private static void UIntToBytes(uint value, byte[] destination, int destinationOffset)
{
destination[destinationOffset++] = (byte)(value >> 24);
destination[destinationOffset++] = (byte)(value >> 16);
destination[destinationOffset++] = (byte)(value >> 8);
destination[destinationOffset++] = (byte)value;
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Security.Cryptography;
using System.Text;
using System.Security;
namespace Lidgren.Network
{
/// <summary>
/// Methods to encrypt and decrypt data using the XTEA algorithm
/// </summary>
public sealed class NetXtea : NetBlockEncryptionBase
{
private const int c_blockSize = 8;
private const int c_keySize = 16;
private const int c_delta = unchecked((int)0x9E3779B9);
private readonly int m_numRounds;
private readonly uint[] m_sum0;
private readonly uint[] m_sum1;
/// <summary>
/// Gets the block size for this cipher
/// </summary>
public override int BlockSize { get { return c_blockSize; } }
/// <summary>
/// 16 byte key
/// </summary>
public NetXtea(byte[] key, int rounds)
{
if (key.Length < c_keySize)
throw new NetException("Key too short!");
m_numRounds = rounds;
m_sum0 = new uint[m_numRounds];
m_sum1 = new uint[m_numRounds];
uint[] tmp = new uint[8];
int num2;
int index = num2 = 0;
while (index < 4)
{
tmp[index] = BitConverter.ToUInt32(key, num2);
index++;
num2 += 4;
}
for (index = num2 = 0; index < 32; index++)
{
m_sum0[index] = ((uint)num2) + tmp[num2 & 3];
num2 += -1640531527;
m_sum1[index] = ((uint)num2) + tmp[(num2 >> 11) & 3];
}
}
/// <summary>
/// 16 byte key
/// </summary>
public NetXtea(byte[] key)
: this(key, 32)
{
}
/// <summary>
/// String to hash for key
/// </summary>
public NetXtea(string key)
: this(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key)), 32)
{
}
/// <summary>
/// Encrypts a block of bytes
/// </summary>
protected override void EncryptBlock(byte[] source, int sourceOffset, byte[] destination)
{
uint v0 = BytesToUInt(source, sourceOffset);
uint v1 = BytesToUInt(source, sourceOffset + 4);
for (int i = 0; i != m_numRounds; i++)
{
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
}
UIntToBytes(v0, destination, 0);
UIntToBytes(v1, destination, 0 + 4);
return;
}
/// <summary>
/// Decrypts a block of bytes
/// </summary>
protected override void DecryptBlock(byte[] source, int sourceOffset, byte[] destination)
{
// Pack bytes into integers
uint v0 = BytesToUInt(source, sourceOffset);
uint v1 = BytesToUInt(source, sourceOffset + 4);
for (int i = m_numRounds - 1; i >= 0; i--)
{
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
}
UIntToBytes(v0, destination, 0);
UIntToBytes(v1, destination, 0 + 4);
return;
}
private static uint BytesToUInt(byte[] bytes, int offset)
{
uint retval = (uint)(bytes[offset] << 24);
retval |= (uint)(bytes[++offset] << 16);
retval |= (uint)(bytes[++offset] << 8);
return (retval | bytes[++offset]);
}
private static void UIntToBytes(uint value, byte[] destination, int destinationOffset)
{
destination[destinationOffset++] = (byte)(value >> 24);
destination[destinationOffset++] = (byte)(value >> 16);
destination[destinationOffset++] = (byte)(value >> 8);
destination[destinationOffset++] = (byte)value;
}
}
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<PropertyGroup>
<ProjectType>Local</ProjectType>
@@ -39,7 +39,7 @@
<DebugSymbols>True</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<Optimize>False</Optimize>
<OutputPath>..\bin\</OutputPath>
<OutputPath>$(SolutionDir)bin\Lidgren\</OutputPath>
<RegisterForComInterop>False</RegisterForComInterop>
<RemoveIntegerChecks>False</RemoveIntegerChecks>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
@@ -62,7 +62,7 @@
<DebugSymbols>False</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<Optimize>True</Optimize>
<OutputPath>..\bin\</OutputPath>
<OutputPath>$(SolutionDir)bin\Lidgren\</OutputPath>
<RegisterForComInterop>False</RegisterForComInterop>
<RemoveIntegerChecks>False</RemoveIntegerChecks>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
@@ -107,18 +107,6 @@
<Compile Include="Encryption\NetBlockEncryptionBase.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetDESEncryption.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetRC2Encryption.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetTripleDESEncryption.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetXorEncryption.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetXteaEncryption.cs">
<SubType>Code</SubType>
</Compile>
@@ -297,7 +285,7 @@
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>

View File

@@ -1,14 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Lidgren Network Library
/// </summary>
internal class NamespaceDoc
{
// <include file='_Namespace.xml' path='Documentation/*' />
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Lidgren Network Library
/// </summary>
internal class NamespaceDoc
{
// <include file='_Namespace.xml' path='Documentation/*' />
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,172 +1,172 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Fixed size vector of booleans
/// </summary>
public sealed class NetBitVector
{
private readonly int m_capacity;
private readonly int[] m_data;
private int m_numBitsSet;
/// <summary>
/// Gets the number of bits/booleans stored in this vector
/// </summary>
public int Capacity { get { return m_capacity; } }
/// <summary>
/// NetBitVector constructor
/// </summary>
public NetBitVector(int bitsCapacity)
{
m_capacity = bitsCapacity;
m_data = new int[(bitsCapacity + 31) / 32];
}
/// <summary>
/// Returns true if all bits/booleans are set to zero/false
/// </summary>
public bool IsEmpty()
{
return (m_numBitsSet == 0);
}
/// <summary>
/// Returns the number of bits/booleans set to one/true
/// </summary>
/// <returns></returns>
public int Count()
{
return m_numBitsSet;
}
/// <summary>
/// Shift all bits one step down, cycling the first bit to the top
/// </summary>
public void RotateDown()
{
int lenMinusOne = m_data.Length - 1;
int firstBit = m_data[0] & 1;
for (int i = 0; i < lenMinusOne; i++)
m_data[i] = ((m_data[i] >> 1) & ~(1 << 31)) | m_data[i + 1] << 31;
int lastIndex = m_capacity - 1 - (32 * lenMinusOne);
// special handling of last int
int cur = m_data[lenMinusOne];
cur = cur >> 1;
cur |= firstBit << lastIndex;
m_data[lenMinusOne] = cur;
}
/// <summary>
/// Gets the first (lowest) index set to true
/// </summary>
public int GetFirstSetIndex()
{
int idx = 0;
int data = m_data[0];
while (data == 0)
{
idx++;
data = m_data[idx];
}
int a = 0;
while (((data >> a) & 1) == 0)
a++;
return (idx * 32) + a;
}
/// <summary>
/// Gets the bit/bool at the specified index
/// </summary>
public bool Get(int bitIndex)
{
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
return (m_data[bitIndex / 32] & (1 << (bitIndex % 32))) != 0;
}
/// <summary>
/// Sets or clears the bit/bool at the specified index
/// </summary>
public void Set(int bitIndex, bool value)
{
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
int idx = bitIndex / 32;
if (value)
{
if ((m_data[idx] & (1 << (bitIndex % 32))) == 0)
m_numBitsSet++;
m_data[idx] |= (1 << (bitIndex % 32));
}
else
{
if ((m_data[idx] & (1 << (bitIndex % 32))) != 0)
m_numBitsSet--;
m_data[idx] &= (~(1 << (bitIndex % 32)));
}
}
/// <summary>
/// Gets the bit/bool at the specified index
/// </summary>
[System.Runtime.CompilerServices.IndexerName("Bit")]
public bool this[int index]
{
get { return Get(index); }
set { Set(index, value); }
}
/// <summary>
/// Sets all bits/booleans to zero/false
/// </summary>
public void Clear()
{
Array.Clear(m_data, 0, m_data.Length);
m_numBitsSet = 0;
NetException.Assert(this.IsEmpty());
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
StringBuilder bdr = new StringBuilder(m_capacity + 2);
bdr.Append('[');
for (int i = 0; i < m_capacity; i++)
bdr.Append(Get(m_capacity - i - 1) ? '1' : '0');
bdr.Append(']');
return bdr.ToString();
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Fixed size vector of booleans
/// </summary>
public sealed class NetBitVector
{
private readonly int m_capacity;
private readonly int[] m_data;
private int m_numBitsSet;
/// <summary>
/// Gets the number of bits/booleans stored in this vector
/// </summary>
public int Capacity { get { return m_capacity; } }
/// <summary>
/// NetBitVector constructor
/// </summary>
public NetBitVector(int bitsCapacity)
{
m_capacity = bitsCapacity;
m_data = new int[(bitsCapacity + 31) / 32];
}
/// <summary>
/// Returns true if all bits/booleans are set to zero/false
/// </summary>
public bool IsEmpty()
{
return (m_numBitsSet == 0);
}
/// <summary>
/// Returns the number of bits/booleans set to one/true
/// </summary>
/// <returns></returns>
public int Count()
{
return m_numBitsSet;
}
/// <summary>
/// Shift all bits one step down, cycling the first bit to the top
/// </summary>
public void RotateDown()
{
int lenMinusOne = m_data.Length - 1;
int firstBit = m_data[0] & 1;
for (int i = 0; i < lenMinusOne; i++)
m_data[i] = ((m_data[i] >> 1) & ~(1 << 31)) | m_data[i + 1] << 31;
int lastIndex = m_capacity - 1 - (32 * lenMinusOne);
// special handling of last int
int cur = m_data[lenMinusOne];
cur = cur >> 1;
cur |= firstBit << lastIndex;
m_data[lenMinusOne] = cur;
}
/// <summary>
/// Gets the first (lowest) index set to true
/// </summary>
public int GetFirstSetIndex()
{
int idx = 0;
int data = m_data[0];
while (data == 0)
{
idx++;
data = m_data[idx];
}
int a = 0;
while (((data >> a) & 1) == 0)
a++;
return (idx * 32) + a;
}
/// <summary>
/// Gets the bit/bool at the specified index
/// </summary>
public bool Get(int bitIndex)
{
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
return (m_data[bitIndex / 32] & (1 << (bitIndex % 32))) != 0;
}
/// <summary>
/// Sets or clears the bit/bool at the specified index
/// </summary>
public void Set(int bitIndex, bool value)
{
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
int idx = bitIndex / 32;
if (value)
{
if ((m_data[idx] & (1 << (bitIndex % 32))) == 0)
m_numBitsSet++;
m_data[idx] |= (1 << (bitIndex % 32));
}
else
{
if ((m_data[idx] & (1 << (bitIndex % 32))) != 0)
m_numBitsSet--;
m_data[idx] &= (~(1 << (bitIndex % 32)));
}
}
/// <summary>
/// Gets the bit/bool at the specified index
/// </summary>
[System.Runtime.CompilerServices.IndexerName("Bit")]
public bool this[int index]
{
get { return Get(index); }
set { Set(index, value); }
}
/// <summary>
/// Sets all bits/booleans to zero/false
/// </summary>
public void Clear()
{
Array.Clear(m_data, 0, m_data.Length);
m_numBitsSet = 0;
NetException.Assert(this.IsEmpty());
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
StringBuilder bdr = new StringBuilder(m_capacity + 2);
bdr.Append('[');
for (int i = 0; i < m_capacity; i++)
bdr.Append(Get(m_capacity - i - 1) ? '1' : '0');
bdr.Append(']');
return bdr.ToString();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,312 +1,311 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Net;
namespace Lidgren.Network
{
public partial class NetBuffer
{
/// <summary>
/// Gets the internal data buffer
/// </summary>
public byte[] PeekDataBuffer() { return m_data; }
//
// 1 bit
//
/// <summary>
/// Reads a 1-bit Boolean without advancing the read pointer
/// </summary>
public bool PeekBoolean()
{
NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition);
return (retval > 0 ? true : false);
}
//
// 8 bit
//
/// <summary>
/// Reads a Byte without advancing the read pointer
/// </summary>
public byte PeekByte()
{
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
return retval;
}
/// <summary>
/// Reads an SByte without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public sbyte PeekSByte()
{
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
return (sbyte)retval;
}
/// <summary>
/// Reads the specified number of bits into a Byte without advancing the read pointer
/// </summary>
public byte PeekByte(int numberOfBits)
{
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
return retval;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public byte[] PeekBytes(int numberOfBytes)
{
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
byte[] retval = new byte[numberOfBytes];
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0);
return retval;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public void PeekBytes(byte[] into, int offset, int numberOfBytes)
{
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
NetException.Assert(offset + numberOfBytes <= into.Length);
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset);
return;
}
//
// 16 bit
//
/// <summary>
/// Reads an Int16 without advancing the read pointer
/// </summary>
public Int16 PeekInt16()
{
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition);
return (short)retval;
}
/// <summary>
/// Reads a UInt16 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt16 PeekUInt16()
{
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition);
return (ushort)retval;
}
//
// 32 bit
//
/// <summary>
/// Reads an Int32 without advancing the read pointer
/// </summary>
public Int32 PeekInt32()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
return (Int32)retval;
}
/// <summary>
/// Reads the specified number of bits into an Int32 without advancing the read pointer
/// </summary>
public Int32 PeekInt32(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadInt() can only read between 1 and 32 bits");
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
if (numberOfBits == 32)
return (int)retval;
int signBit = 1 << (numberOfBits - 1);
if ((retval & signBit) == 0)
return (int)retval; // positive
// negative
unchecked
{
uint mask = ((uint)-1) >> (33 - numberOfBits);
uint tmp = (retval & mask) + 1;
return -((int)tmp);
}
}
/// <summary>
/// Reads a UInt32 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt32 PeekUInt32()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
return retval;
}
/// <summary>
/// Reads the specified number of bits into a UInt32 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt32 PeekUInt32(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadUInt() can only read between 1 and 32 bits");
//NetException.Assert(m_bitLength - m_readBitPtr >= numberOfBits, "tried to read past buffer size");
UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
return retval;
}
//
// 64 bit
//
/// <summary>
/// Reads a UInt64 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt64 PeekUInt64()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition + 32);
ulong retval = low + (high << 32);
return retval;
}
/// <summary>
/// Reads an Int64 without advancing the read pointer
/// </summary>
public Int64 PeekInt64()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
unchecked
{
ulong retval = PeekUInt64();
long longRetval = (long)retval;
return longRetval;
}
}
/// <summary>
/// Reads the specified number of bits into an UInt64 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt64 PeekUInt64(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 64), "ReadUInt() can only read between 1 and 64 bits");
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
ulong retval;
if (numberOfBits <= 32)
{
retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
}
else
{
retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32;
}
return retval;
}
/// <summary>
/// Reads the specified number of bits into an Int64 without advancing the read pointer
/// </summary>
public Int64 PeekInt64(int numberOfBits)
{
NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits");
return (long)PeekUInt64(numberOfBits);
}
//
// Floating point
//
/// <summary>
/// Reads a 32-bit Single without advancing the read pointer
/// </summary>
public float PeekFloat()
{
return PeekSingle();
}
/// <summary>
/// Reads a 32-bit Single without advancing the read pointer
/// </summary>
public float PeekSingle()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
if ((m_readPosition & 7) == 0) // read directly
{
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
return retval;
}
byte[] bytes = PeekBytes(4);
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
/// Reads a 64-bit Double without advancing the read pointer
/// </summary>
public double PeekDouble()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
if ((m_readPosition & 7) == 0) // read directly
{
// read directly
double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3);
return retval;
}
byte[] bytes = PeekBytes(8);
return BitConverter.ToDouble(bytes, 0);
}
/// <summary>
/// Reads a string without advancing the read pointer
/// </summary>
public string PeekString()
{
int wasReadPosition = m_readPosition;
string retval = ReadString();
m_readPosition = wasReadPosition;
return retval;
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Net;
namespace Lidgren.Network
{
public partial class NetBuffer
{
/// <summary>
/// Gets the internal data buffer
/// </summary>
public byte[] PeekDataBuffer() { return m_data; }
//
// 1 bit
//
/// <summary>
/// Reads a 1-bit Boolean without advancing the read pointer
/// </summary>
public bool PeekBoolean()
{
NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition);
return (retval > 0 ? true : false);
}
//
// 8 bit
//
/// <summary>
/// Reads a Byte without advancing the read pointer
/// </summary>
public byte PeekByte()
{
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
return retval;
}
/// <summary>
/// Reads an SByte without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public sbyte PeekSByte()
{
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
return (sbyte)retval;
}
/// <summary>
/// Reads the specified number of bits into a Byte without advancing the read pointer
/// </summary>
public byte PeekByte(int numberOfBits)
{
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
return retval;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public byte[] PeekBytes(int numberOfBytes)
{
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
byte[] retval = new byte[numberOfBytes];
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0);
return retval;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public void PeekBytes(byte[] into, int offset, int numberOfBytes)
{
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
NetException.Assert(offset + numberOfBytes <= into.Length);
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset);
return;
}
//
// 16 bit
//
/// <summary>
/// Reads an Int16 without advancing the read pointer
/// </summary>
public Int16 PeekInt16()
{
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition);
return (short)retval;
}
/// <summary>
/// Reads a UInt16 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt16 PeekUInt16()
{
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition);
return (ushort)retval;
}
//
// 32 bit
//
/// <summary>
/// Reads an Int32 without advancing the read pointer
/// </summary>
public Int32 PeekInt32()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
return (Int32)retval;
}
/// <summary>
/// Reads the specified number of bits into an Int32 without advancing the read pointer
/// </summary>
public Int32 PeekInt32(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadInt() can only read between 1 and 32 bits");
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
if (numberOfBits == 32)
return (int)retval;
int signBit = 1 << (numberOfBits - 1);
if ((retval & signBit) == 0)
return (int)retval; // positive
// negative
unchecked
{
uint mask = ((uint)-1) >> (33 - numberOfBits);
uint tmp = (retval & mask) + 1;
return -((int)tmp);
}
}
/// <summary>
/// Reads a UInt32 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt32 PeekUInt32()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
return retval;
}
/// <summary>
/// Reads the specified number of bits into a UInt32 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt32 PeekUInt32(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadUInt() can only read between 1 and 32 bits");
UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
return retval;
}
//
// 64 bit
//
/// <summary>
/// Reads a UInt64 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt64 PeekUInt64()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition + 32);
ulong retval = low + (high << 32);
return retval;
}
/// <summary>
/// Reads an Int64 without advancing the read pointer
/// </summary>
public Int64 PeekInt64()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
unchecked
{
ulong retval = PeekUInt64();
long longRetval = (long)retval;
return longRetval;
}
}
/// <summary>
/// Reads the specified number of bits into an UInt64 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt64 PeekUInt64(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 64), "ReadUInt() can only read between 1 and 64 bits");
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
ulong retval;
if (numberOfBits <= 32)
{
retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
}
else
{
retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32;
}
return retval;
}
/// <summary>
/// Reads the specified number of bits into an Int64 without advancing the read pointer
/// </summary>
public Int64 PeekInt64(int numberOfBits)
{
NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits");
return (long)PeekUInt64(numberOfBits);
}
//
// Floating point
//
/// <summary>
/// Reads a 32-bit Single without advancing the read pointer
/// </summary>
public float PeekFloat()
{
return PeekSingle();
}
/// <summary>
/// Reads a 32-bit Single without advancing the read pointer
/// </summary>
public float PeekSingle()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
if ((m_readPosition & 7) == 0) // read directly
{
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
return retval;
}
byte[] bytes = PeekBytes(4);
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
/// Reads a 64-bit Double without advancing the read pointer
/// </summary>
public double PeekDouble()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
if ((m_readPosition & 7) == 0) // read directly
{
// read directly
double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3);
return retval;
}
byte[] bytes = PeekBytes(8);
return BitConverter.ToDouble(bytes, 0);
}
/// <summary>
/// Reads a string without advancing the read pointer
/// </summary>
public string PeekString()
{
int wasReadPosition = m_readPosition;
string retval = ReadString();
m_readPosition = wasReadPosition;
return retval;
}
}
}

View File

@@ -1,103 +1,103 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetBuffer
{
/// <summary>
/// Reads all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void ReadAllFields(object target)
{
ReadAllFields(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Reads all fields with the specified binding of the object in alphabetical order using reflection
/// </summary>
public void ReadAllFields(object target, BindingFlags flags)
{
if (target == null)
return;
Type tp = target.GetType();
FieldInfo[] fields = tp.GetFields(flags);
NetUtility.SortMembersList(fields);
foreach (FieldInfo fi in fields)
{
object value;
// find read method
MethodInfo readMethod;
if (s_readMethods.TryGetValue(fi.FieldType, out readMethod))
{
// read value
value = readMethod.Invoke(this, null);
// set the value
fi.SetValue(target, value);
}
}
}
/// <summary>
/// Reads all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void ReadAllProperties(object target)
{
ReadAllProperties(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Reads all fields with the specified binding of the object in alphabetical order using reflection
/// </summary>
public void ReadAllProperties(object target, BindingFlags flags)
{
if (target == null)
throw new ArgumentNullException("target");
if (target == null)
return;
Type tp = target.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
NetUtility.SortMembersList(fields);
foreach (PropertyInfo fi in fields)
{
object value;
// find read method
MethodInfo readMethod;
if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod))
{
// read value
value = readMethod.Invoke(this, null);
// set the value
MethodInfo setMethod = fi.GetSetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
setMethod.Invoke(target, new object[] { value });
}
}
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetBuffer
{
/// <summary>
/// Reads all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void ReadAllFields(object target)
{
ReadAllFields(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Reads all fields with the specified binding of the object in alphabetical order using reflection
/// </summary>
public void ReadAllFields(object target, BindingFlags flags)
{
if (target == null)
return;
Type tp = target.GetType();
FieldInfo[] fields = tp.GetFields(flags);
NetUtility.SortMembersList(fields);
foreach (FieldInfo fi in fields)
{
object value;
// find read method
MethodInfo readMethod;
if (s_readMethods.TryGetValue(fi.FieldType, out readMethod))
{
// read value
value = readMethod.Invoke(this, null);
// set the value
fi.SetValue(target, value);
}
}
}
/// <summary>
/// Reads all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void ReadAllProperties(object target)
{
ReadAllProperties(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Reads all fields with the specified binding of the object in alphabetical order using reflection
/// </summary>
public void ReadAllProperties(object target, BindingFlags flags)
{
if (target == null)
throw new ArgumentNullException("target");
if (target == null)
return;
Type tp = target.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
NetUtility.SortMembersList(fields);
foreach (PropertyInfo fi in fields)
{
object value;
// find read method
MethodInfo readMethod;
if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod))
{
// read value
value = readMethod.Invoke(this, null);
// set the value
MethodInfo setMethod = fi.GetSetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
setMethod.Invoke(target, new object[] { value });
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,91 +1,91 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetBuffer
{
/// <summary>
/// Writes all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void WriteAllFields(object ob)
{
WriteAllFields(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Writes all fields with specified binding in alphabetical order using reflection
/// </summary>
public void WriteAllFields(object ob, BindingFlags flags)
{
if (ob == null)
return;
Type tp = ob.GetType();
FieldInfo[] fields = tp.GetFields(flags);
NetUtility.SortMembersList(fields);
foreach (FieldInfo fi in fields)
{
object value = fi.GetValue(ob);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.FieldType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
else
throw new NetException("Failed to find write method for type " + fi.FieldType);
}
}
/// <summary>
/// Writes all public and private declared instance properties of the object in alphabetical order using reflection
/// </summary>
public void WriteAllProperties(object ob)
{
WriteAllProperties(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Writes all properties with specified binding in alphabetical order using reflection
/// </summary>
public void WriteAllProperties(object ob, BindingFlags flags)
{
if (ob == null)
return;
Type tp = ob.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
NetUtility.SortMembersList(fields);
foreach (PropertyInfo fi in fields)
{
MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
object value = getMethod.Invoke(ob, null);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetBuffer
{
/// <summary>
/// Writes all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void WriteAllFields(object ob)
{
WriteAllFields(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Writes all fields with specified binding in alphabetical order using reflection
/// </summary>
public void WriteAllFields(object ob, BindingFlags flags)
{
if (ob == null)
return;
Type tp = ob.GetType();
FieldInfo[] fields = tp.GetFields(flags);
NetUtility.SortMembersList(fields);
foreach (FieldInfo fi in fields)
{
object value = fi.GetValue(ob);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.FieldType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
else
throw new NetException("Failed to find write method for type " + fi.FieldType);
}
}
/// <summary>
/// Writes all public and private declared instance properties of the object in alphabetical order using reflection
/// </summary>
public void WriteAllProperties(object ob)
{
WriteAllProperties(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Writes all properties with specified binding in alphabetical order using reflection
/// </summary>
public void WriteAllProperties(object ob, BindingFlags flags)
{
if (ob == null)
return;
Type tp = ob.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
NetUtility.SortMembersList(fields);
foreach (PropertyInfo fi in fields)
{
MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
object value = getMethod.Invoke(ob, null);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,100 +1,100 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetBuffer
{
/// <summary>
/// Number of bytes to overallocate for each message to avoid resizing
/// </summary>
protected const int c_overAllocateAmount = 4;
private static readonly Dictionary<Type, MethodInfo> s_readMethods;
private static readonly Dictionary<Type, MethodInfo> s_writeMethods;
internal byte[] m_data;
internal int m_bitLength;
internal int m_readPosition;
/// <summary>
/// Gets or sets the internal data buffer
/// </summary>
public byte[] Data
{
get { return m_data; }
set { m_data = value; }
}
/// <summary>
/// Gets or sets the length of the used portion of the buffer in bytes
/// </summary>
public int LengthBytes
{
get { return ((m_bitLength + 7) >> 3); }
set
{
m_bitLength = value * 8;
InternalEnsureBufferSize(m_bitLength);
}
}
/// <summary>
/// Gets or sets the length of the used portion of the buffer in bits
/// </summary>
public int LengthBits
{
get { return m_bitLength; }
set
{
m_bitLength = value;
InternalEnsureBufferSize(m_bitLength);
}
}
/// <summary>
/// Gets or sets the read position in the buffer, in bits (not bytes)
/// </summary>
public long Position
{
get { return (long)m_readPosition; }
set { m_readPosition = (int)value; }
}
/// <summary>
/// Gets the position in the buffer in bytes; note that the bits of the first returned byte may already have been read - check the Position property to make sure.
/// </summary>
public int PositionInBytes
{
get { return (int)(m_readPosition / 8); }
}
static NetBuffer()
{
Type[] integralTypes = typeof(Byte).Assembly.GetTypes();
s_readMethods = new Dictionary<Type, MethodInfo>();
MethodInfo[] methods = typeof(NetIncomingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (MethodInfo mi in methods)
{
if (mi.GetParameters().Length == 0 && mi.Name.StartsWith("Read", StringComparison.InvariantCulture) && mi.Name.Substring(4) == mi.ReturnType.Name)
{
s_readMethods[mi.ReturnType] = mi;
}
}
s_writeMethods = new Dictionary<Type, MethodInfo>();
methods = typeof(NetOutgoingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (MethodInfo mi in methods)
{
if (mi.Name.Equals("Write", StringComparison.InvariantCulture))
{
ParameterInfo[] pis = mi.GetParameters();
if (pis.Length == 1)
s_writeMethods[pis[0].ParameterType] = mi;
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetBuffer
{
/// <summary>
/// Number of bytes to overallocate for each message to avoid resizing
/// </summary>
protected const int c_overAllocateAmount = 4;
private static readonly Dictionary<Type, MethodInfo> s_readMethods;
private static readonly Dictionary<Type, MethodInfo> s_writeMethods;
internal byte[] m_data;
internal int m_bitLength;
internal int m_readPosition;
/// <summary>
/// Gets or sets the internal data buffer
/// </summary>
public byte[] Data
{
get { return m_data; }
set { m_data = value; }
}
/// <summary>
/// Gets or sets the length of the used portion of the buffer in bytes
/// </summary>
public int LengthBytes
{
get { return ((m_bitLength + 7) >> 3); }
set
{
m_bitLength = value * 8;
InternalEnsureBufferSize(m_bitLength);
}
}
/// <summary>
/// Gets or sets the length of the used portion of the buffer in bits
/// </summary>
public int LengthBits
{
get { return m_bitLength; }
set
{
m_bitLength = value;
InternalEnsureBufferSize(m_bitLength);
}
}
/// <summary>
/// Gets or sets the read position in the buffer, in bits (not bytes)
/// </summary>
public long Position
{
get { return (long)m_readPosition; }
set { m_readPosition = (int)value; }
}
/// <summary>
/// Gets the position in the buffer in bytes; note that the bits of the first returned byte may already have been read - check the Position property to make sure.
/// </summary>
public int PositionInBytes
{
get { return (int)(m_readPosition / 8); }
}
static NetBuffer()
{
Type[] integralTypes = typeof(Byte).Assembly.GetTypes();
s_readMethods = new Dictionary<Type, MethodInfo>();
MethodInfo[] methods = typeof(NetIncomingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (MethodInfo mi in methods)
{
if (mi.GetParameters().Length == 0 && mi.Name.StartsWith("Read", StringComparison.InvariantCulture) && mi.Name.Substring(4) == mi.ReturnType.Name)
{
s_readMethods[mi.ReturnType] = mi;
}
}
s_writeMethods = new Dictionary<Type, MethodInfo>();
methods = typeof(NetOutgoingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (MethodInfo mi in methods)
{
if (mi.Name.Equals("Write", StringComparison.InvariantCulture))
{
ParameterInfo[] pis = mi.GetParameters();
if (pis.Length == 1)
s_writeMethods[pis[0].ParameterType] = mi;
}
}
}
}
}

View File

@@ -1,171 +1,171 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Net;
namespace Lidgren.Network
{
/// <summary>
/// Specialized version of NetPeer used for a "client" connection. It does not accept any incoming connections and maintains a ServerConnection property
/// </summary>
public class NetClient : NetPeer
{
/// <summary>
/// Gets the connection to the server, if any
/// </summary>
public NetConnection ServerConnection
{
get
{
NetConnection retval = null;
if (m_connections.Count > 0)
{
try
{
retval = m_connections[0];
}
catch
{
// preempted!
return null;
}
}
return retval;
}
}
/// <summary>
/// Gets the connection status of the server connection (or NetConnectionStatus.Disconnected if no connection)
/// </summary>
public NetConnectionStatus ConnectionStatus
{
get
{
var conn = ServerConnection;
if (conn == null)
return NetConnectionStatus.Disconnected;
return conn.Status;
}
}
/// <summary>
/// NetClient constructor
/// </summary>
/// <param name="config"></param>
public NetClient(NetPeerConfiguration config)
: base(config)
{
config.AcceptIncomingConnections = false;
}
/// <summary>
/// Connect to a remote server
/// </summary>
/// <param name="remoteEndPoint">The remote endpoint to connect to</param>
/// <param name="hailMessage">The hail message to pass</param>
/// <returns>server connection, or null if already connected</returns>
public override NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
{
lock (m_connections)
{
if (m_connections.Count > 0)
{
LogWarning("Connect attempt failed; Already connected");
return null;
}
}
lock (m_handshakes)
{
if (m_handshakes.Count > 0)
{
LogWarning("Connect attempt failed; Handshake already in progress");
return null;
}
}
return base.Connect(remoteEndPoint, hailMessage);
}
/// <summary>
/// Disconnect from server
/// </summary>
/// <param name="byeMessage">reason for disconnect</param>
public void Disconnect(string byeMessage)
{
NetConnection serverConnection = ServerConnection;
if (serverConnection == null)
{
lock (m_handshakes)
{
if (m_handshakes.Count > 0)
{
LogVerbose("Aborting connection attempt");
foreach(var hs in m_handshakes)
hs.Value.Disconnect(byeMessage);
return;
}
}
LogWarning("Disconnect requested when not connected!");
return;
}
serverConnection.Disconnect(byeMessage);
}
/// <summary>
/// Sends message to server
/// </summary>
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
{
NetConnection serverConnection = ServerConnection;
if (serverConnection == null)
{
LogWarning("Cannot send message, no server connection!");
return NetSendResult.FailedNotConnected;
}
return serverConnection.SendMessage(msg, method, 0);
}
/// <summary>
/// Sends message to server
/// </summary>
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
{
NetConnection serverConnection = ServerConnection;
if (serverConnection == null)
{
LogWarning("Cannot send message, no server connection!");
return NetSendResult.FailedNotConnected;
}
return serverConnection.SendMessage(msg, method, sequenceChannel);
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
return "[NetClient " + ServerConnection + "]";
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Net;
namespace Lidgren.Network
{
/// <summary>
/// Specialized version of NetPeer used for a "client" connection. It does not accept any incoming connections and maintains a ServerConnection property
/// </summary>
public class NetClient : NetPeer
{
/// <summary>
/// Gets the connection to the server, if any
/// </summary>
public NetConnection ServerConnection
{
get
{
NetConnection retval = null;
if (m_connections.Count > 0)
{
try
{
retval = m_connections[0];
}
catch
{
// preempted!
return null;
}
}
return retval;
}
}
/// <summary>
/// Gets the connection status of the server connection (or NetConnectionStatus.Disconnected if no connection)
/// </summary>
public NetConnectionStatus ConnectionStatus
{
get
{
var conn = ServerConnection;
if (conn == null)
return NetConnectionStatus.Disconnected;
return conn.Status;
}
}
/// <summary>
/// NetClient constructor
/// </summary>
/// <param name="config"></param>
public NetClient(NetPeerConfiguration config)
: base(config)
{
config.AcceptIncomingConnections = false;
}
/// <summary>
/// Connect to a remote server
/// </summary>
/// <param name="remoteEndPoint">The remote endpoint to connect to</param>
/// <param name="hailMessage">The hail message to pass</param>
/// <returns>server connection, or null if already connected</returns>
public override NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
{
lock (m_connections)
{
if (m_connections.Count > 0)
{
LogWarning("Connect attempt failed; Already connected");
return null;
}
}
lock (m_handshakes)
{
if (m_handshakes.Count > 0)
{
LogWarning("Connect attempt failed; Handshake already in progress");
return null;
}
}
return base.Connect(remoteEndPoint, hailMessage);
}
/// <summary>
/// Disconnect from server
/// </summary>
/// <param name="byeMessage">reason for disconnect</param>
public void Disconnect(string byeMessage)
{
NetConnection serverConnection = ServerConnection;
if (serverConnection == null)
{
lock (m_handshakes)
{
if (m_handshakes.Count > 0)
{
LogVerbose("Aborting connection attempt");
foreach(var hs in m_handshakes)
hs.Value.Disconnect(byeMessage);
return;
}
}
LogWarning("Disconnect requested when not connected!");
return;
}
serverConnection.Disconnect(byeMessage);
}
/// <summary>
/// Sends message to server
/// </summary>
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
{
NetConnection serverConnection = ServerConnection;
if (serverConnection == null)
{
LogWarning("Cannot send message, no server connection!");
return NetSendResult.FailedNotConnected;
}
return serverConnection.SendMessage(msg, method, 0);
}
/// <summary>
/// Sends message to server
/// </summary>
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
{
NetConnection serverConnection = ServerConnection;
if (serverConnection == null)
{
LogWarning("Cannot send message, no server connection!");
return NetSendResult.FailedNotConnected;
}
return serverConnection.SendMessage(msg, method, sequenceChannel);
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
return "[NetClient " + ServerConnection + "]";
}
}
}

View File

@@ -1,479 +1,477 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
public partial class NetConnection
{
internal bool m_connectRequested;
internal bool m_disconnectRequested;
internal bool m_connectionInitiator;
internal string m_disconnectMessage;
internal NetIncomingMessage m_remoteHailMessage;
internal float m_lastHandshakeSendTime;
internal int m_handshakeAttempts;
/// <summary>
/// The message that the remote part specified via Connect() or Approve() - can be null.
/// </summary>
public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } }
// heartbeat called when connection still is in m_handshakes of NetPeer
internal void UnconnectedHeartbeat(float now)
{
m_peer.VerifyNetworkThread();
if (m_disconnectRequested)
ExecuteDisconnect(m_disconnectMessage, true);
if (m_connectRequested)
{
switch (m_status)
{
case NetConnectionStatus.Connected:
case NetConnectionStatus.RespondedConnect:
// reconnect
ExecuteDisconnect("Reconnecting", true);
break;
case NetConnectionStatus.InitiatedConnect:
// send another connect attempt
SendConnect(now);
break;
case NetConnectionStatus.Disconnected:
throw new NetException("This connection is Disconnected; spent. A new one should have been created");
case NetConnectionStatus.Disconnecting:
// let disconnect finish first
break;
case NetConnectionStatus.None:
default:
SendConnect(now);
break;
}
return;
}
if (now - m_lastHandshakeSendTime > m_peerConfiguration.m_resendHandshakeInterval)
{
if (m_handshakeAttempts >= m_peerConfiguration.m_maximumHandshakeAttempts)
{
// failed to connect
ExecuteDisconnect("Failed to establish connection - no response from remote host", true);
return;
}
// resend handshake
switch (m_status)
{
case NetConnectionStatus.InitiatedConnect:
SendConnect(now);
break;
case NetConnectionStatus.RespondedConnect:
SendConnectResponse(now, true);
break;
case NetConnectionStatus.None:
case NetConnectionStatus.ReceivedInitiation:
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
break;
case NetConnectionStatus.RespondedAwaitingApproval:
// awaiting approval
m_lastHandshakeSendTime = now; // postpone handshake resend
break;
default:
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
break;
}
}
}
internal void ExecuteDisconnect(string reason, bool sendByeMessage)
{
m_peer.VerifyNetworkThread();
//m_peer.LogDebug("Executing disconnect");
// clear send queues
for (int i = 0; i < m_sendChannels.Length; i++)
{
NetSenderChannelBase channel = m_sendChannels[i];
if (channel != null)
channel.Reset();
}
if (sendByeMessage)
SendDisconnect(reason, true);
SetStatus(NetConnectionStatus.Disconnected, reason);
// in case we're still in handshake
lock (m_peer.m_handshakes)
m_peer.m_handshakes.Remove(m_remoteEndPoint);
m_disconnectRequested = false;
m_connectRequested = false;
m_handshakeAttempts = 0;
}
internal void SendConnect(float now)
{
m_peer.VerifyNetworkThread();
int preAllocate = 13 + m_peerConfiguration.AppIdentifier.Length;
preAllocate += (m_localHailMessage == null ? 0 : m_localHailMessage.LengthBytes);
NetOutgoingMessage om = m_peer.CreateMessage(preAllocate);
om.m_messageType = NetMessageType.Connect;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier);
om.Write(now);
WriteLocalHail(om);
m_peer.SendLibrary(om, m_remoteEndPoint);
m_connectRequested = false;
m_lastHandshakeSendTime = now;
m_handshakeAttempts++;
if (m_handshakeAttempts > 1)
m_peer.LogDebug("Resending Connect...");
SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
}
internal void SendConnectResponse(float now, bool onLibraryThread)
{
if (onLibraryThread)
m_peer.VerifyNetworkThread();
NetOutgoingMessage om = m_peer.CreateMessage(m_peerConfiguration.AppIdentifier.Length + 13 + (m_localHailMessage == null ? 0 : m_localHailMessage.LengthBytes));
om.m_messageType = NetMessageType.ConnectResponse;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier);
om.Write(now);
WriteLocalHail(om);
if (onLibraryThread)
m_peer.SendLibrary(om, m_remoteEndPoint);
else
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
m_lastHandshakeSendTime = now;
m_handshakeAttempts++;
if (m_handshakeAttempts > 1)
m_peer.LogDebug("Resending ConnectResponse...");
SetStatus(NetConnectionStatus.RespondedConnect, "Remotely requested connect");
}
internal void SendDisconnect(string reason, bool onLibraryThread)
{
if (onLibraryThread)
m_peer.VerifyNetworkThread();
NetOutgoingMessage om = m_peer.CreateMessage(reason);
om.m_messageType = NetMessageType.Disconnect;
if (onLibraryThread)
m_peer.SendLibrary(om, m_remoteEndPoint);
else
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
}
private void WriteLocalHail(NetOutgoingMessage om)
{
if (m_localHailMessage != null)
{
byte[] hi = m_localHailMessage.Data;
if (hi != null && hi.Length >= m_localHailMessage.LengthBytes)
{
if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10)
throw new NetException("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes));
om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes);
}
}
}
internal void SendConnectionEstablished()
{
NetOutgoingMessage om = m_peer.CreateMessage(4);
om.m_messageType = NetMessageType.ConnectionEstablished;
om.Write((float)NetTime.Now);
m_peer.SendLibrary(om, m_remoteEndPoint);
m_handshakeAttempts = 0;
InitializePing();
if (m_status != NetConnectionStatus.Connected)
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
}
/// <summary>
/// Approves this connection; sending a connection response to the remote host
/// </summary>
public void Approve()
{
if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
{
m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
return;
}
m_localHailMessage = null;
m_handshakeAttempts = 0;
SendConnectResponse((float)NetTime.Now, false);
}
/// <summary>
/// Approves this connection; sending a connection response to the remote host
/// </summary>
/// <param name="localHail">The local hail message that will be set as RemoteHailMessage on the remote host</param>
public void Approve(NetOutgoingMessage localHail)
{
if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
{
m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
return;
}
m_localHailMessage = localHail;
m_handshakeAttempts = 0;
SendConnectResponse((float)NetTime.Now, false);
}
/// <summary>
/// Denies this connection; disconnecting it
/// </summary>
public void Deny()
{
Deny(string.Empty);
}
/// <summary>
/// Denies this connection; disconnecting it
/// </summary>
/// <param name="reason">The stated reason for the disconnect, readable as a string in the StatusChanged message on the remote host</param>
public void Deny(string reason)
{
// send disconnect; remove from handshakes
SendDisconnect(reason, false);
// remove from handshakes
m_peer.m_handshakes.Remove(m_remoteEndPoint); // TODO: make this more thread safe? we're on user thread
}
internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payloadLength)
{
m_peer.VerifyNetworkThread();
byte[] hail;
switch (tp)
{
case NetMessageType.Connect:
if (m_status == NetConnectionStatus.ReceivedInitiation)
{
// Whee! Server full has already been checked
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
if (ok)
{
if (hail != null)
{
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
m_remoteHailMessage.LengthBits = (hail.Length * 8);
}
else
{
m_remoteHailMessage = null;
}
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
{
// ok, let's not add connection just yet
NetIncomingMessage appMsg = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, (m_remoteHailMessage == null ? 0 : m_remoteHailMessage.LengthBytes));
appMsg.m_receiveTime = now;
appMsg.m_senderConnection = this;
appMsg.m_senderEndPoint = this.m_remoteEndPoint;
if (m_remoteHailMessage != null)
appMsg.Write(m_remoteHailMessage.m_data, 0, m_remoteHailMessage.LengthBytes);
SetStatus(NetConnectionStatus.RespondedAwaitingApproval, "Awaiting approval");
m_peer.ReleaseMessage(appMsg);
return;
}
SendConnectResponse((float)now, true);
}
return;
}
if (m_status == NetConnectionStatus.RespondedAwaitingApproval)
{
m_peer.LogWarning("Ignoring multiple Connect() most likely due to a delayed Approval");
return;
}
if (m_status == NetConnectionStatus.RespondedConnect)
{
// our ConnectResponse must have been lost
SendConnectResponse((float)now, true);
return;
}
m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength);
break;
case NetMessageType.ConnectResponse:
switch (m_status)
{
case NetConnectionStatus.InitiatedConnect:
// awesome
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
if (ok)
{
if (hail != null)
{
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
m_remoteHailMessage.LengthBits = (hail.Length * 8);
}
else
{
m_remoteHailMessage = null;
}
m_peer.AcceptConnection(this);
SendConnectionEstablished();
return;
}
break;
case NetConnectionStatus.RespondedConnect:
// hello, wtf?
break;
case NetConnectionStatus.Disconnecting:
case NetConnectionStatus.Disconnected:
case NetConnectionStatus.ReceivedInitiation:
case NetConnectionStatus.None:
// wtf? anyway, bye!
break;
case NetConnectionStatus.Connected:
// my ConnectionEstablished must have been lost, send another one
SendConnectionEstablished();
return;
}
break;
case NetMessageType.ConnectionEstablished:
switch (m_status)
{
case NetConnectionStatus.Connected:
// ok...
break;
case NetConnectionStatus.Disconnected:
case NetConnectionStatus.Disconnecting:
case NetConnectionStatus.None:
// too bad, almost made it
break;
case NetConnectionStatus.ReceivedInitiation:
// uh, a little premature... ignore
break;
case NetConnectionStatus.InitiatedConnect:
// weird, should have been RespondedConnect...
break;
case NetConnectionStatus.RespondedConnect:
// awesome
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
InitializeRemoteTimeOffset(msg.ReadSingle());
m_peer.AcceptConnection(this);
InitializePing();
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
return;
}
break;
case NetMessageType.Disconnect:
// ouch
string reason = "Ouch";
try
{
NetIncomingMessage inc = m_peer.SetupReadHelperMessage(ptr, payloadLength);
reason = inc.ReadString();
}
catch
{
}
ExecuteDisconnect(reason, false);
break;
case NetMessageType.Discovery:
m_peer.HandleIncomingDiscoveryRequest(now, m_remoteEndPoint, ptr, payloadLength);
return;
case NetMessageType.DiscoveryResponse:
m_peer.HandleIncomingDiscoveryResponse(now, m_remoteEndPoint, ptr, payloadLength);
return;
case NetMessageType.Ping:
// silently ignore
return;
default:
m_peer.LogDebug("Unhandled type during handshake: " + tp + " length: " + payloadLength);
break;
}
}
private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail)
{
hail = null;
// create temporary incoming message
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
try
{
string remoteAppIdentifier = msg.ReadString();
long remoteUniqueIdentifier = msg.ReadInt64();
InitializeRemoteTimeOffset(msg.ReadSingle());
int remainingBytes = payloadLength - (msg.PositionInBytes - ptr);
if (remainingBytes > 0)
hail = msg.ReadBytes(remainingBytes);
if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier)
{
// wrong app identifier
ExecuteDisconnect("Wrong application identifier!", true);
return false;
}
m_remoteUniqueIdentifier = remoteUniqueIdentifier;
}
catch(Exception ex)
{
// whatever; we failed
ExecuteDisconnect("Handshake data validation failed", true);
m_peer.LogWarning("ReadRemoteHandshakeData failed: " + ex.Message);
return false;
}
return true;
}
/// <summary>
/// Disconnect from the remote peer
/// </summary>
/// <param name="byeMessage">the message to send with the disconnect message</param>
public void Disconnect(string byeMessage)
{
// user or library thread
if (m_status == NetConnectionStatus.None || m_status == NetConnectionStatus.Disconnected)
return;
m_peer.LogVerbose("Disconnect requested for " + this);
m_disconnectMessage = byeMessage;
if (m_status != NetConnectionStatus.Disconnected && m_status != NetConnectionStatus.None)
SetStatus(NetConnectionStatus.Disconnecting, byeMessage);
m_handshakeAttempts = 0;
m_disconnectRequested = true;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
public partial class NetConnection
{
internal bool m_connectRequested;
internal bool m_disconnectRequested;
internal bool m_connectionInitiator;
internal string m_disconnectMessage;
internal NetIncomingMessage m_remoteHailMessage;
internal float m_lastHandshakeSendTime;
internal int m_handshakeAttempts;
/// <summary>
/// The message that the remote part specified via Connect() or Approve() - can be null.
/// </summary>
public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } }
// heartbeat called when connection still is in m_handshakes of NetPeer
internal void UnconnectedHeartbeat(float now)
{
m_peer.VerifyNetworkThread();
if (m_disconnectRequested)
ExecuteDisconnect(m_disconnectMessage, true);
if (m_connectRequested)
{
switch (m_status)
{
case NetConnectionStatus.Connected:
case NetConnectionStatus.RespondedConnect:
// reconnect
ExecuteDisconnect("Reconnecting", true);
break;
case NetConnectionStatus.InitiatedConnect:
// send another connect attempt
SendConnect(now);
break;
case NetConnectionStatus.Disconnected:
throw new NetException("This connection is Disconnected; spent. A new one should have been created");
case NetConnectionStatus.Disconnecting:
// let disconnect finish first
break;
case NetConnectionStatus.None:
default:
SendConnect(now);
break;
}
return;
}
if (now - m_lastHandshakeSendTime > m_peerConfiguration.m_resendHandshakeInterval)
{
if (m_handshakeAttempts >= m_peerConfiguration.m_maximumHandshakeAttempts)
{
// failed to connect
ExecuteDisconnect("Failed to establish connection - no response from remote host", true);
return;
}
// resend handshake
switch (m_status)
{
case NetConnectionStatus.InitiatedConnect:
SendConnect(now);
break;
case NetConnectionStatus.RespondedConnect:
SendConnectResponse(now, true);
break;
case NetConnectionStatus.None:
case NetConnectionStatus.ReceivedInitiation:
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
break;
case NetConnectionStatus.RespondedAwaitingApproval:
// awaiting approval
m_lastHandshakeSendTime = now; // postpone handshake resend
break;
default:
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
break;
}
}
}
internal void ExecuteDisconnect(string reason, bool sendByeMessage)
{
m_peer.VerifyNetworkThread();
// clear send queues
for (int i = 0; i < m_sendChannels.Length; i++)
{
NetSenderChannelBase channel = m_sendChannels[i];
if (channel != null)
channel.Reset();
}
if (sendByeMessage)
SendDisconnect(reason, true);
SetStatus(NetConnectionStatus.Disconnected, reason);
// in case we're still in handshake
lock (m_peer.m_handshakes)
m_peer.m_handshakes.Remove(m_remoteEndPoint);
m_disconnectRequested = false;
m_connectRequested = false;
m_handshakeAttempts = 0;
}
internal void SendConnect(float now)
{
m_peer.VerifyNetworkThread();
int preAllocate = 13 + m_peerConfiguration.AppIdentifier.Length;
preAllocate += (m_localHailMessage == null ? 0 : m_localHailMessage.LengthBytes);
NetOutgoingMessage om = m_peer.CreateMessage(preAllocate);
om.m_messageType = NetMessageType.Connect;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier);
om.Write(now);
WriteLocalHail(om);
m_peer.SendLibrary(om, m_remoteEndPoint);
m_connectRequested = false;
m_lastHandshakeSendTime = now;
m_handshakeAttempts++;
if (m_handshakeAttempts > 1)
m_peer.LogDebug("Resending Connect...");
SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
}
internal void SendConnectResponse(float now, bool onLibraryThread)
{
if (onLibraryThread)
m_peer.VerifyNetworkThread();
NetOutgoingMessage om = m_peer.CreateMessage(m_peerConfiguration.AppIdentifier.Length + 13 + (m_localHailMessage == null ? 0 : m_localHailMessage.LengthBytes));
om.m_messageType = NetMessageType.ConnectResponse;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier);
om.Write(now);
WriteLocalHail(om);
if (onLibraryThread)
m_peer.SendLibrary(om, m_remoteEndPoint);
else
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
m_lastHandshakeSendTime = now;
m_handshakeAttempts++;
if (m_handshakeAttempts > 1)
m_peer.LogDebug("Resending ConnectResponse...");
SetStatus(NetConnectionStatus.RespondedConnect, "Remotely requested connect");
}
internal void SendDisconnect(string reason, bool onLibraryThread)
{
if (onLibraryThread)
m_peer.VerifyNetworkThread();
NetOutgoingMessage om = m_peer.CreateMessage(reason);
om.m_messageType = NetMessageType.Disconnect;
if (onLibraryThread)
m_peer.SendLibrary(om, m_remoteEndPoint);
else
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
}
private void WriteLocalHail(NetOutgoingMessage om)
{
if (m_localHailMessage != null)
{
byte[] hi = m_localHailMessage.Data;
if (hi != null && hi.Length >= m_localHailMessage.LengthBytes)
{
if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10)
throw new NetException("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes));
om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes);
}
}
}
internal void SendConnectionEstablished()
{
NetOutgoingMessage om = m_peer.CreateMessage(4);
om.m_messageType = NetMessageType.ConnectionEstablished;
om.Write((float)NetTime.Now);
m_peer.SendLibrary(om, m_remoteEndPoint);
m_handshakeAttempts = 0;
InitializePing();
if (m_status != NetConnectionStatus.Connected)
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
}
/// <summary>
/// Approves this connection; sending a connection response to the remote host
/// </summary>
public void Approve()
{
if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
{
m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
return;
}
m_localHailMessage = null;
m_handshakeAttempts = 0;
SendConnectResponse((float)NetTime.Now, false);
}
/// <summary>
/// Approves this connection; sending a connection response to the remote host
/// </summary>
/// <param name="localHail">The local hail message that will be set as RemoteHailMessage on the remote host</param>
public void Approve(NetOutgoingMessage localHail)
{
if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
{
m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
return;
}
m_localHailMessage = localHail;
m_handshakeAttempts = 0;
SendConnectResponse((float)NetTime.Now, false);
}
/// <summary>
/// Denies this connection; disconnecting it
/// </summary>
public void Deny()
{
Deny(string.Empty);
}
/// <summary>
/// Denies this connection; disconnecting it
/// </summary>
/// <param name="reason">The stated reason for the disconnect, readable as a string in the StatusChanged message on the remote host</param>
public void Deny(string reason)
{
// send disconnect; remove from handshakes
SendDisconnect(reason, false);
// remove from handshakes
m_peer.m_handshakes.Remove(m_remoteEndPoint); // TODO: make this more thread safe? we're on user thread
}
internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payloadLength)
{
m_peer.VerifyNetworkThread();
byte[] hail;
switch (tp)
{
case NetMessageType.Connect:
if (m_status == NetConnectionStatus.ReceivedInitiation)
{
// Whee! Server full has already been checked
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
if (ok)
{
if (hail != null)
{
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
m_remoteHailMessage.LengthBits = (hail.Length * 8);
}
else
{
m_remoteHailMessage = null;
}
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
{
// ok, let's not add connection just yet
NetIncomingMessage appMsg = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, (m_remoteHailMessage == null ? 0 : m_remoteHailMessage.LengthBytes));
appMsg.m_receiveTime = now;
appMsg.m_senderConnection = this;
appMsg.m_senderEndPoint = this.m_remoteEndPoint;
if (m_remoteHailMessage != null)
appMsg.Write(m_remoteHailMessage.m_data, 0, m_remoteHailMessage.LengthBytes);
SetStatus(NetConnectionStatus.RespondedAwaitingApproval, "Awaiting approval");
m_peer.ReleaseMessage(appMsg);
return;
}
SendConnectResponse((float)now, true);
}
return;
}
if (m_status == NetConnectionStatus.RespondedAwaitingApproval)
{
m_peer.LogWarning("Ignoring multiple Connect() most likely due to a delayed Approval");
return;
}
if (m_status == NetConnectionStatus.RespondedConnect)
{
// our ConnectResponse must have been lost
SendConnectResponse((float)now, true);
return;
}
m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength);
break;
case NetMessageType.ConnectResponse:
switch (m_status)
{
case NetConnectionStatus.InitiatedConnect:
// awesome
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
if (ok)
{
if (hail != null)
{
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
m_remoteHailMessage.LengthBits = (hail.Length * 8);
}
else
{
m_remoteHailMessage = null;
}
m_peer.AcceptConnection(this);
SendConnectionEstablished();
return;
}
break;
case NetConnectionStatus.RespondedConnect:
// hello, wtf?
break;
case NetConnectionStatus.Disconnecting:
case NetConnectionStatus.Disconnected:
case NetConnectionStatus.ReceivedInitiation:
case NetConnectionStatus.None:
// wtf? anyway, bye!
break;
case NetConnectionStatus.Connected:
// my ConnectionEstablished must have been lost, send another one
SendConnectionEstablished();
return;
}
break;
case NetMessageType.ConnectionEstablished:
switch (m_status)
{
case NetConnectionStatus.Connected:
// ok...
break;
case NetConnectionStatus.Disconnected:
case NetConnectionStatus.Disconnecting:
case NetConnectionStatus.None:
// too bad, almost made it
break;
case NetConnectionStatus.ReceivedInitiation:
// uh, a little premature... ignore
break;
case NetConnectionStatus.InitiatedConnect:
// weird, should have been RespondedConnect...
break;
case NetConnectionStatus.RespondedConnect:
// awesome
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
InitializeRemoteTimeOffset(msg.ReadSingle());
m_peer.AcceptConnection(this);
InitializePing();
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
return;
}
break;
case NetMessageType.Disconnect:
// ouch
string reason = "Ouch";
try
{
NetIncomingMessage inc = m_peer.SetupReadHelperMessage(ptr, payloadLength);
reason = inc.ReadString();
}
catch
{
}
ExecuteDisconnect(reason, false);
break;
case NetMessageType.Discovery:
m_peer.HandleIncomingDiscoveryRequest(now, m_remoteEndPoint, ptr, payloadLength);
return;
case NetMessageType.DiscoveryResponse:
m_peer.HandleIncomingDiscoveryResponse(now, m_remoteEndPoint, ptr, payloadLength);
return;
case NetMessageType.Ping:
// silently ignore
return;
default:
m_peer.LogDebug("Unhandled type during handshake: " + tp + " length: " + payloadLength);
break;
}
}
private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail)
{
hail = null;
// create temporary incoming message
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
try
{
string remoteAppIdentifier = msg.ReadString();
long remoteUniqueIdentifier = msg.ReadInt64();
InitializeRemoteTimeOffset(msg.ReadSingle());
int remainingBytes = payloadLength - (msg.PositionInBytes - ptr);
if (remainingBytes > 0)
hail = msg.ReadBytes(remainingBytes);
if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier)
{
// wrong app identifier
ExecuteDisconnect("Wrong application identifier!", true);
return false;
}
m_remoteUniqueIdentifier = remoteUniqueIdentifier;
}
catch(Exception ex)
{
// whatever; we failed
ExecuteDisconnect("Handshake data validation failed", true);
m_peer.LogWarning("ReadRemoteHandshakeData failed: " + ex.Message);
return false;
}
return true;
}
/// <summary>
/// Disconnect from the remote peer
/// </summary>
/// <param name="byeMessage">the message to send with the disconnect message</param>
public void Disconnect(string byeMessage)
{
// user or library thread
if (m_status == NetConnectionStatus.None || m_status == NetConnectionStatus.Disconnected)
return;
m_peer.LogVerbose("Disconnect requested for " + this);
m_disconnectMessage = byeMessage;
if (m_status != NetConnectionStatus.Disconnected && m_status != NetConnectionStatus.None)
SetStatus(NetConnectionStatus.Disconnecting, byeMessage);
m_handshakeAttempts = 0;
m_disconnectRequested = true;
}
}
}

View File

@@ -1,147 +1,145 @@
using System;
namespace Lidgren.Network
{
public partial class NetConnection
{
private float m_sentPingTime;
private int m_sentPingNumber;
private float m_averageRoundtripTime;
private float m_timeoutDeadline = float.MaxValue;
// local time value + m_remoteTimeOffset = remote time value
internal double m_remoteTimeOffset;
/// <summary>
/// Gets the current average roundtrip time in seconds
/// </summary>
public float AverageRoundtripTime { get { return m_averageRoundtripTime; } }
/// <summary>
/// Time offset between this peer and the remote peer
/// </summary>
public float RemoteTimeOffset { get { return (float)m_remoteTimeOffset; } }
// this might happen more than once
internal void InitializeRemoteTimeOffset(float remoteSendTime)
{
m_remoteTimeOffset = (remoteSendTime + (m_averageRoundtripTime / 2.0)) - NetTime.Now;
}
/// <summary>
/// Gets local time value comparable to NetTime.Now from a remote value
/// </summary>
public double GetLocalTime(double remoteTimestamp)
{
return remoteTimestamp - m_remoteTimeOffset;
}
/// <summary>
/// Gets the remote time value for a local time value produced by NetTime.Now
/// </summary>
public double GetRemoteTime(double localTimestamp)
{
return localTimestamp + m_remoteTimeOffset;
}
internal void InitializePing()
{
float now = (float)NetTime.Now;
// randomize ping sent time (0.25 - 1.0 x ping interval)
m_sentPingTime = now;
m_sentPingTime -= (m_peerConfiguration.PingInterval * 0.25f); // delay ping for a little while
m_sentPingTime -= (NetRandom.Instance.NextSingle() * (m_peerConfiguration.PingInterval * 0.75f));
m_timeoutDeadline = now + (m_peerConfiguration.m_connectionTimeout * 2.0f); // initially allow a little more time
// make it better, quick :-)
SendPing();
}
internal void SendPing()
{
m_peer.VerifyNetworkThread();
m_sentPingNumber++;
m_sentPingTime = (float)NetTime.Now;
NetOutgoingMessage om = m_peer.CreateMessage(1);
om.Write((byte)m_sentPingNumber); // truncating to 0-255
om.m_messageType = NetMessageType.Ping;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1);
}
internal void SendPong(int pingNumber)
{
m_peer.VerifyNetworkThread();
NetOutgoingMessage om = m_peer.CreateMessage(5);
om.Write((byte)pingNumber);
om.Write((float)NetTime.Now); // we should update this value to reflect the exact point in time the packet is SENT
om.m_messageType = NetMessageType.Pong;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1);
}
internal void ReceivedPong(float now, int pongNumber, float remoteSendTime)
{
if ((byte)pongNumber != (byte)m_sentPingNumber)
{
m_peer.LogVerbose("Ping/Pong mismatch; dropped message?");
return;
}
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
float rtt = now - m_sentPingTime;
NetException.Assert(rtt >= 0);
double diff = (remoteSendTime + (rtt / 2.0)) - now;
if (m_averageRoundtripTime < 0)
{
m_remoteTimeOffset = diff;
m_averageRoundtripTime = rtt;
m_peer.LogDebug("Initiated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + " Remote time is: " + (now + diff));
}
else
{
m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f);
m_remoteTimeOffset = ((m_remoteTimeOffset * (double)(m_sentPingNumber - 1)) + diff) / (double)m_sentPingNumber;
m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + ", remote time to " + (now + m_remoteTimeOffset) + " (ie. diff " + m_remoteTimeOffset + ")");
}
// update resend delay for all channels
float resendDelay = GetResendDelay();
foreach (var chan in m_sendChannels)
{
var rchan = chan as NetReliableSenderChannel;
if (rchan != null)
rchan.m_resendDelay = resendDelay;
}
// m_peer.LogVerbose("Timeout deadline pushed to " + m_timeoutDeadline);
// notify the application that average rtt changed
if (m_peer.m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionLatencyUpdated))
{
NetIncomingMessage update = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionLatencyUpdated, 4);
update.m_senderConnection = this;
update.m_senderEndPoint = this.m_remoteEndPoint;
update.Write(rtt);
m_peer.ReleaseMessage(update);
}
}
}
}
using System;
namespace Lidgren.Network
{
public partial class NetConnection
{
private float m_sentPingTime;
private int m_sentPingNumber;
private float m_averageRoundtripTime;
private float m_timeoutDeadline = float.MaxValue;
// local time value + m_remoteTimeOffset = remote time value
internal double m_remoteTimeOffset;
/// <summary>
/// Gets the current average roundtrip time in seconds
/// </summary>
public float AverageRoundtripTime { get { return m_averageRoundtripTime; } }
/// <summary>
/// Time offset between this peer and the remote peer
/// </summary>
public float RemoteTimeOffset { get { return (float)m_remoteTimeOffset; } }
// this might happen more than once
internal void InitializeRemoteTimeOffset(float remoteSendTime)
{
m_remoteTimeOffset = (remoteSendTime + (m_averageRoundtripTime / 2.0)) - NetTime.Now;
}
/// <summary>
/// Gets local time value comparable to NetTime.Now from a remote value
/// </summary>
public double GetLocalTime(double remoteTimestamp)
{
return remoteTimestamp - m_remoteTimeOffset;
}
/// <summary>
/// Gets the remote time value for a local time value produced by NetTime.Now
/// </summary>
public double GetRemoteTime(double localTimestamp)
{
return localTimestamp + m_remoteTimeOffset;
}
internal void InitializePing()
{
float now = (float)NetTime.Now;
// randomize ping sent time (0.25 - 1.0 x ping interval)
m_sentPingTime = now;
m_sentPingTime -= (m_peerConfiguration.PingInterval * 0.25f); // delay ping for a little while
m_sentPingTime -= (NetRandom.Instance.NextSingle() * (m_peerConfiguration.PingInterval * 0.75f));
m_timeoutDeadline = now + (m_peerConfiguration.m_connectionTimeout * 2.0f); // initially allow a little more time
// make it better, quick :-)
SendPing();
}
internal void SendPing()
{
m_peer.VerifyNetworkThread();
m_sentPingNumber++;
m_sentPingTime = (float)NetTime.Now;
NetOutgoingMessage om = m_peer.CreateMessage(1);
om.Write((byte)m_sentPingNumber); // truncating to 0-255
om.m_messageType = NetMessageType.Ping;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1);
}
internal void SendPong(int pingNumber)
{
m_peer.VerifyNetworkThread();
NetOutgoingMessage om = m_peer.CreateMessage(5);
om.Write((byte)pingNumber);
om.Write((float)NetTime.Now); // we should update this value to reflect the exact point in time the packet is SENT
om.m_messageType = NetMessageType.Pong;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1);
}
internal void ReceivedPong(float now, int pongNumber, float remoteSendTime)
{
if ((byte)pongNumber != (byte)m_sentPingNumber)
{
m_peer.LogVerbose("Ping/Pong mismatch; dropped message?");
return;
}
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
float rtt = now - m_sentPingTime;
NetException.Assert(rtt >= 0);
double diff = (remoteSendTime + (rtt / 2.0)) - now;
if (m_averageRoundtripTime < 0)
{
m_remoteTimeOffset = diff;
m_averageRoundtripTime = rtt;
m_peer.LogDebug("Initiated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + " Remote time is: " + (now + diff));
}
else
{
m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f);
m_remoteTimeOffset = ((m_remoteTimeOffset * (double)(m_sentPingNumber - 1)) + diff) / (double)m_sentPingNumber;
m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + ", remote time to " + (now + m_remoteTimeOffset) + " (ie. diff " + m_remoteTimeOffset + ")");
}
// update resend delay for all channels
float resendDelay = GetResendDelay();
foreach (var chan in m_sendChannels)
{
var rchan = chan as NetReliableSenderChannel;
if (rchan != null)
rchan.m_resendDelay = resendDelay;
}
// notify the application that average rtt changed
if (m_peer.m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionLatencyUpdated))
{
NetIncomingMessage update = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionLatencyUpdated, 4);
update.m_senderConnection = this;
update.m_senderEndPoint = this.m_remoteEndPoint;
update.Write(rtt);
m_peer.ReleaseMessage(update);
}
}
}
}

View File

@@ -1,175 +1,166 @@
using System;
namespace Lidgren.Network
{
public partial class NetConnection
{
private enum ExpandMTUStatus
{
None,
InProgress,
Finished
}
private const int c_protocolMaxMTU = (int)((((float)ushort.MaxValue / 8.0f) - 1.0f));
private ExpandMTUStatus m_expandMTUStatus;
private int m_largestSuccessfulMTU;
private int m_smallestFailedMTU;
private int m_lastSentMTUAttemptSize;
private double m_lastSentMTUAttemptTime;
private int m_mtuAttemptFails;
internal int m_currentMTU;
internal void InitExpandMTU(double now)
{
m_lastSentMTUAttemptTime = now + m_peerConfiguration.m_expandMTUFrequency + 1.5f + m_averageRoundtripTime; // wait a tiny bit before starting to expand mtu
m_largestSuccessfulMTU = 512;
m_smallestFailedMTU = -1;
m_currentMTU = m_peerConfiguration.MaximumTransmissionUnit;
}
private void MTUExpansionHeartbeat(double now)
{
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
return;
if (m_expandMTUStatus == ExpandMTUStatus.None)
{
if (m_peerConfiguration.m_autoExpandMTU == false)
{
FinalizeMTU(m_currentMTU);
return;
}
// begin expansion
ExpandMTU(now, true);
return;
}
if (now > m_lastSentMTUAttemptTime + m_peerConfiguration.ExpandMTUFrequency)
{
m_mtuAttemptFails++;
if (m_mtuAttemptFails == 3)
{
FinalizeMTU(m_currentMTU);
return;
}
// timed out; ie. failed
m_smallestFailedMTU = m_lastSentMTUAttemptSize;
ExpandMTU(now, false);
}
}
private void ExpandMTU(double now, bool succeeded)
{
int tryMTU;
// we've nevered encountered failure
if (m_smallestFailedMTU == -1)
{
// we've never encountered failure; expand by 25% each time
tryMTU = (int)((float)m_currentMTU * 1.25f);
//m_peer.LogDebug("Trying MTU " + tryMTU);
}
else
{
// we HAVE encountered failure; so try in between
tryMTU = (int)(((float)m_smallestFailedMTU + (float)m_largestSuccessfulMTU) / 2.0f);
//m_peer.LogDebug("Trying MTU " + m_smallestFailedMTU + " <-> " + m_largestSuccessfulMTU + " = " + tryMTU);
}
if (tryMTU > c_protocolMaxMTU)
tryMTU = c_protocolMaxMTU;
if (tryMTU == m_largestSuccessfulMTU)
{
//m_peer.LogDebug("Found optimal MTU - exiting");
FinalizeMTU(m_largestSuccessfulMTU);
return;
}
SendExpandMTU(now, tryMTU);
}
private void SendExpandMTU(double now, int size)
{
NetOutgoingMessage om = m_peer.CreateMessage(size);
byte[] tmp = new byte[size];
om.Write(tmp);
om.m_messageType = NetMessageType.ExpandMTURequest;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool ok = m_peer.SendMTUPacket(len, m_remoteEndPoint);
if (ok == false)
{
//m_peer.LogDebug("Send MTU failed for size " + size);
// failure
if (m_smallestFailedMTU == -1 || size < m_smallestFailedMTU)
{
m_smallestFailedMTU = size;
m_mtuAttemptFails++;
if (m_mtuAttemptFails >= m_peerConfiguration.ExpandMTUFailAttempts)
{
FinalizeMTU(m_largestSuccessfulMTU);
return;
}
}
ExpandMTU(now, false);
return;
}
m_lastSentMTUAttemptSize = size;
m_lastSentMTUAttemptTime = now;
m_statistics.PacketSent(len, 1);
}
private void FinalizeMTU(int size)
{
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
return;
m_expandMTUStatus = ExpandMTUStatus.Finished;
m_currentMTU = size;
if (m_currentMTU != m_peerConfiguration.m_maximumTransmissionUnit)
m_peer.LogDebug("Expanded Maximum Transmission Unit to: " + m_currentMTU + " bytes");
return;
}
private void SendMTUSuccess(int size)
{
NetOutgoingMessage om = m_peer.CreateMessage(4);
om.Write(size);
om.m_messageType = NetMessageType.ExpandMTUSuccess;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
// m_peer.LogDebug("Received MTU expand request for " + size + " bytes");
m_statistics.PacketSent(len, 1);
}
private void HandleExpandMTUSuccess(double now, int size)
{
if (size > m_largestSuccessfulMTU)
m_largestSuccessfulMTU = size;
if (size < m_currentMTU)
{
//m_peer.LogDebug("Received low MTU expand success (size " + size + "); current mtu is " + m_currentMTU);
return;
}
//m_peer.LogDebug("Expanding MTU to " + size);
m_currentMTU = size;
ExpandMTU(now, true);
}
}
}
using System;
namespace Lidgren.Network
{
public partial class NetConnection
{
private enum ExpandMTUStatus
{
None,
InProgress,
Finished
}
private const int c_protocolMaxMTU = (int)((((float)ushort.MaxValue / 8.0f) - 1.0f));
private ExpandMTUStatus m_expandMTUStatus;
private int m_largestSuccessfulMTU;
private int m_smallestFailedMTU;
private int m_lastSentMTUAttemptSize;
private double m_lastSentMTUAttemptTime;
private int m_mtuAttemptFails;
internal int m_currentMTU;
internal void InitExpandMTU(double now)
{
m_lastSentMTUAttemptTime = now + m_peerConfiguration.m_expandMTUFrequency + 1.5f + m_averageRoundtripTime; // wait a tiny bit before starting to expand mtu
m_largestSuccessfulMTU = 512;
m_smallestFailedMTU = -1;
m_currentMTU = m_peerConfiguration.MaximumTransmissionUnit;
}
private void MTUExpansionHeartbeat(double now)
{
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
return;
if (m_expandMTUStatus == ExpandMTUStatus.None)
{
if (m_peerConfiguration.m_autoExpandMTU == false)
{
FinalizeMTU(m_currentMTU);
return;
}
// begin expansion
ExpandMTU(now, true);
return;
}
if (now > m_lastSentMTUAttemptTime + m_peerConfiguration.ExpandMTUFrequency)
{
m_mtuAttemptFails++;
if (m_mtuAttemptFails == 3)
{
FinalizeMTU(m_currentMTU);
return;
}
// timed out; ie. failed
m_smallestFailedMTU = m_lastSentMTUAttemptSize;
ExpandMTU(now, false);
}
}
private void ExpandMTU(double now, bool succeeded)
{
int tryMTU;
// we've nevered encountered failure
if (m_smallestFailedMTU == -1)
{
// we've never encountered failure; expand by 25% each time
tryMTU = (int)((float)m_currentMTU * 1.25f);
}
else
{
// we HAVE encountered failure; so try in between
tryMTU = (int)(((float)m_smallestFailedMTU + (float)m_largestSuccessfulMTU) / 2.0f);
}
if (tryMTU > c_protocolMaxMTU)
tryMTU = c_protocolMaxMTU;
if (tryMTU == m_largestSuccessfulMTU)
{
FinalizeMTU(m_largestSuccessfulMTU);
return;
}
SendExpandMTU(now, tryMTU);
}
private void SendExpandMTU(double now, int size)
{
NetOutgoingMessage om = m_peer.CreateMessage(size);
byte[] tmp = new byte[size];
om.Write(tmp);
om.m_messageType = NetMessageType.ExpandMTURequest;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool ok = m_peer.SendMTUPacket(len, m_remoteEndPoint);
if (ok == false)
{
// failure
if (m_smallestFailedMTU == -1 || size < m_smallestFailedMTU)
{
m_smallestFailedMTU = size;
m_mtuAttemptFails++;
if (m_mtuAttemptFails >= m_peerConfiguration.ExpandMTUFailAttempts)
{
FinalizeMTU(m_largestSuccessfulMTU);
return;
}
}
ExpandMTU(now, false);
return;
}
m_lastSentMTUAttemptSize = size;
m_lastSentMTUAttemptTime = now;
m_statistics.PacketSent(len, 1);
}
private void FinalizeMTU(int size)
{
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
return;
m_expandMTUStatus = ExpandMTUStatus.Finished;
m_currentMTU = size;
if (m_currentMTU != m_peerConfiguration.m_maximumTransmissionUnit)
m_peer.LogDebug("Expanded Maximum Transmission Unit to: " + m_currentMTU + " bytes");
return;
}
private void SendMTUSuccess(int size)
{
NetOutgoingMessage om = m_peer.CreateMessage(4);
om.Write(size);
om.m_messageType = NetMessageType.ExpandMTUSuccess;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1);
}
private void HandleExpandMTUSuccess(double now, int size)
{
if (size > m_largestSuccessfulMTU)
m_largestSuccessfulMTU = size;
if (size < m_currentMTU)
{
return;
}
m_currentMTU = size;
ExpandMTU(now, true);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,204 +1,201 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Uncomment the line below to get statistics in RELEASE builds
#define USE_RELEASE_STATISTICS
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace Lidgren.Network
{
internal enum MessageResendReason
{
Delay,
HoleInSequence
}
/// <summary>
/// Statistics for a NetConnection instance
/// </summary>
public sealed class NetConnectionStatistics
{
private readonly NetConnection m_connection;
internal int m_sentPackets;
internal int m_receivedPackets;
internal int m_sentMessages;
internal int m_receivedMessages;
internal int m_sentBytes;
internal int m_receivedBytes;
internal int m_resentMessagesDueToDelay;
internal int m_resentMessagesDueToHole;
internal NetConnectionStatistics(NetConnection conn)
{
m_connection = conn;
Reset();
}
internal void Reset()
{
m_sentPackets = 0;
m_receivedPackets = 0;
m_sentBytes = 0;
m_receivedBytes = 0;
}
/// <summary>
/// Gets the number of sent packets for this connection
/// </summary>
public int SentPackets { get { return m_sentPackets; } }
/// <summary>
/// Gets the number of received packets for this connection
/// </summary>
public int ReceivedPackets { get { return m_receivedPackets; } }
/// <summary>
/// Gets the number of sent bytes for this connection
/// </summary>
public int SentBytes { get { return m_sentBytes; } }
/// <summary>
/// Gets the number of received bytes for this connection
/// </summary>
public int ReceivedBytes { get { return m_receivedBytes; } }
/// <summary>
/// Gets the number of resent reliable messages for this connection
/// </summary>
public int ResentMessages { get { return m_resentMessagesDueToHole + m_resentMessagesDueToDelay; } }
// public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } }
#if USE_RELEASE_STATISTICS
internal void PacketSent(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#else
[Conditional("DEBUG")]
internal void PacketSent(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS
internal void PacketReceived(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#else
[Conditional("DEBUG")]
internal void PacketReceived(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS
internal void MessageResent(MessageResendReason reason)
{
if (reason == MessageResendReason.Delay)
m_resentMessagesDueToDelay++;
else
m_resentMessagesDueToHole++;
}
#else
[Conditional("DEBUG")]
internal void MessageResent(MessageResendReason reason)
{
if (reason == MessageResendReason.Delay)
m_resentMessagesDueToDelay++;
else
m_resentMessagesDueToHole++;
}
#endif
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
StringBuilder bdr = new StringBuilder();
//bdr.AppendLine("Average roundtrip time: " + NetTime.ToReadable(m_connection.m_averageRoundtripTime));
bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets");
bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages in " + m_receivedPackets + " packets");
if (m_resentMessagesDueToDelay > 0)
bdr.AppendLine("Resent messages (delay): " + m_resentMessagesDueToDelay);
if (m_resentMessagesDueToDelay > 0)
bdr.AppendLine("Resent messages (holes): " + m_resentMessagesDueToHole);
int numUnsent = 0;
int numStored = 0;
foreach (NetSenderChannelBase sendChan in m_connection.m_sendChannels)
{
if (sendChan == null)
continue;
numUnsent += sendChan.m_queuedSends.Count;
var relSendChan = sendChan as NetReliableSenderChannel;
if (relSendChan != null)
{
for (int i = 0; i < relSendChan.m_storedMessages.Length; i++)
if (relSendChan.m_storedMessages[i].Message != null)
numStored++;
}
}
int numWithheld = 0;
foreach (NetReceiverChannelBase recChan in m_connection.m_receiveChannels)
{
var relRecChan = recChan as NetReliableOrderedReceiver;
if (relRecChan != null)
{
for (int i = 0; i < relRecChan.m_withheldMessages.Length; i++)
if (relRecChan.m_withheldMessages[i] != null)
numWithheld++;
}
}
bdr.AppendLine("Unsent messages: " + numUnsent);
bdr.AppendLine("Stored messages: " + numStored);
bdr.AppendLine("Withheld messages: " + numWithheld);
return bdr.ToString();
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Uncomment the line below to get statistics in RELEASE builds
#define USE_RELEASE_STATISTICS
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace Lidgren.Network
{
internal enum MessageResendReason
{
Delay,
HoleInSequence
}
/// <summary>
/// Statistics for a NetConnection instance
/// </summary>
public sealed class NetConnectionStatistics
{
private readonly NetConnection m_connection;
internal int m_sentPackets;
internal int m_receivedPackets;
internal int m_sentMessages;
internal int m_receivedMessages;
internal int m_sentBytes;
internal int m_receivedBytes;
internal int m_resentMessagesDueToDelay;
internal int m_resentMessagesDueToHole;
internal NetConnectionStatistics(NetConnection conn)
{
m_connection = conn;
Reset();
}
internal void Reset()
{
m_sentPackets = 0;
m_receivedPackets = 0;
m_sentBytes = 0;
m_receivedBytes = 0;
}
/// <summary>
/// Gets the number of sent packets for this connection
/// </summary>
public int SentPackets { get { return m_sentPackets; } }
/// <summary>
/// Gets the number of received packets for this connection
/// </summary>
public int ReceivedPackets { get { return m_receivedPackets; } }
/// <summary>
/// Gets the number of sent bytes for this connection
/// </summary>
public int SentBytes { get { return m_sentBytes; } }
/// <summary>
/// Gets the number of received bytes for this connection
/// </summary>
public int ReceivedBytes { get { return m_receivedBytes; } }
/// <summary>
/// Gets the number of resent reliable messages for this connection
/// </summary>
public int ResentMessages { get { return m_resentMessagesDueToHole + m_resentMessagesDueToDelay; } }
#if USE_RELEASE_STATISTICS
internal void PacketSent(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#else
[Conditional("DEBUG")]
internal void PacketSent(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS
internal void PacketReceived(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#else
[Conditional("DEBUG")]
internal void PacketReceived(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS
internal void MessageResent(MessageResendReason reason)
{
if (reason == MessageResendReason.Delay)
m_resentMessagesDueToDelay++;
else
m_resentMessagesDueToHole++;
}
#else
[Conditional("DEBUG")]
internal void MessageResent(MessageResendReason reason)
{
if (reason == MessageResendReason.Delay)
m_resentMessagesDueToDelay++;
else
m_resentMessagesDueToHole++;
}
#endif
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
StringBuilder bdr = new StringBuilder();
bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets");
bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages in " + m_receivedPackets + " packets");
if (m_resentMessagesDueToDelay > 0)
bdr.AppendLine("Resent messages (delay): " + m_resentMessagesDueToDelay);
if (m_resentMessagesDueToDelay > 0)
bdr.AppendLine("Resent messages (holes): " + m_resentMessagesDueToHole);
int numUnsent = 0;
int numStored = 0;
foreach (NetSenderChannelBase sendChan in m_connection.m_sendChannels)
{
if (sendChan == null)
continue;
numUnsent += sendChan.m_queuedSends.Count;
var relSendChan = sendChan as NetReliableSenderChannel;
if (relSendChan != null)
{
for (int i = 0; i < relSendChan.m_storedMessages.Length; i++)
if (relSendChan.m_storedMessages[i].Message != null)
numStored++;
}
}
int numWithheld = 0;
foreach (NetReceiverChannelBase recChan in m_connection.m_receiveChannels)
{
var relRecChan = recChan as NetReliableOrderedReceiver;
if (relRecChan != null)
{
for (int i = 0; i < relRecChan.m_withheldMessages.Length; i++)
if (relRecChan.m_withheldMessages[i] != null)
numWithheld++;
}
}
bdr.AppendLine("Unsent messages: " + numUnsent);
bdr.AppendLine("Stored messages: " + numStored);
bdr.AppendLine("Withheld messages: " + numWithheld);
return bdr.ToString();
}
}
}

View File

@@ -1,68 +1,68 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace Lidgren.Network
{
/// <summary>
/// Status for a NetConnection instance
/// </summary>
public enum NetConnectionStatus
{
/// <summary>
/// No connection, or attempt, in place
/// </summary>
None,
/// <summary>
/// Connect has been sent; waiting for ConnectResponse
/// </summary>
InitiatedConnect,
/// <summary>
/// Connect was received, but ConnectResponse hasn't been sent yet
/// </summary>
ReceivedInitiation,
/// <summary>
/// Connect was received and ApprovalMessage released to the application; awaiting Approve() or Deny()
/// </summary>
RespondedAwaitingApproval, // We got Connect, released ApprovalMessage
/// <summary>
/// Connect was received and ConnectResponse has been sent; waiting for ConnectionEstablished
/// </summary>
RespondedConnect, // we got Connect, sent ConnectResponse
/// <summary>
/// Connected
/// </summary>
Connected, // we received ConnectResponse (if initiator) or ConnectionEstablished (if passive)
/// <summary>
/// In the process of disconnecting
/// </summary>
Disconnecting,
/// <summary>
/// Disconnected
/// </summary>
Disconnected
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace Lidgren.Network
{
/// <summary>
/// Status for a NetConnection instance
/// </summary>
public enum NetConnectionStatus
{
/// <summary>
/// No connection, or attempt, in place
/// </summary>
None,
/// <summary>
/// Connect has been sent; waiting for ConnectResponse
/// </summary>
InitiatedConnect,
/// <summary>
/// Connect was received, but ConnectResponse hasn't been sent yet
/// </summary>
ReceivedInitiation,
/// <summary>
/// Connect was received and ApprovalMessage released to the application; awaiting Approve() or Deny()
/// </summary>
RespondedAwaitingApproval, // We got Connect, released ApprovalMessage
/// <summary>
/// Connect was received and ConnectResponse has been sent; waiting for ConnectionEstablished
/// </summary>
RespondedConnect, // we got Connect, sent ConnectResponse
/// <summary>
/// Connected
/// </summary>
Connected, // we received ConnectResponse (if initiator) or ConnectionEstablished (if passive)
/// <summary>
/// In the process of disconnecting
/// </summary>
Disconnecting,
/// <summary>
/// Disconnected
/// </summary>
Disconnected
}
}

View File

@@ -1,57 +1,57 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace Lidgren.Network
{
/// <summary>
/// All the constants used when compiling the library
/// </summary>
internal static class NetConstants
{
internal const int NumTotalChannels = 99;
internal const int NetChannelsPerDeliveryMethod = 32;
internal const int NumSequenceNumbers = 1024;
internal const int HeaderByteSize = 5;
internal const int UnreliableWindowSize = 128;
internal const int ReliableOrderedWindowSize = 64;
internal const int ReliableSequencedWindowSize = 64;
internal const int DefaultWindowSize = 64;
internal const int MaxFragmentationGroups = ushort.MaxValue - 1;
internal const int UnfragmentedMessageHeaderSize = 5;
/// <summary>
/// Number of channels which needs a sequence number to work
/// </summary>
internal const int NumSequencedChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced1;
/// <summary>
/// Number of reliable channels
/// </summary>
internal const int NumReliableChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserReliableUnordered;
internal const string ConnResetMessage = "Connection was reset by remote host";
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace Lidgren.Network
{
/// <summary>
/// All the constants used when compiling the library
/// </summary>
internal static class NetConstants
{
internal const int NumTotalChannels = 99;
internal const int NetChannelsPerDeliveryMethod = 32;
internal const int NumSequenceNumbers = 1024;
internal const int HeaderByteSize = 5;
internal const int UnreliableWindowSize = 128;
internal const int ReliableOrderedWindowSize = 64;
internal const int ReliableSequencedWindowSize = 64;
internal const int DefaultWindowSize = 64;
internal const int MaxFragmentationGroups = ushort.MaxValue - 1;
internal const int UnfragmentedMessageHeaderSize = 5;
/// <summary>
/// Number of channels which needs a sequence number to work
/// </summary>
internal const int NumSequencedChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced1;
/// <summary>
/// Number of reliable channels
/// </summary>
internal const int NumReliableChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserReliableUnordered;
internal const string ConnResetMessage = "Connection was reset by remote host";
}
}

View File

@@ -1,46 +1,46 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// How the library deals with resends and handling of late messages
/// </summary>
public enum NetDeliveryMethod : byte
{
//
// Actually a publicly visible subset of NetMessageType
//
/// <summary>
/// Indicates an error
/// </summary>
Unknown = 0,
/// <summary>
/// Unreliable, unordered delivery
/// </summary>
Unreliable = 1,
/// <summary>
/// Unreliable delivery, but automatically dropping late messages
/// </summary>
UnreliableSequenced = 2,
/// <summary>
/// Reliable delivery, but unordered
/// </summary>
ReliableUnordered = 34,
/// <summary>
/// Reliable delivery, except for late messages which are dropped
/// </summary>
ReliableSequenced = 35,
/// <summary>
/// Reliable, ordered delivery
/// </summary>
ReliableOrdered = 67,
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// How the library deals with resends and handling of late messages
/// </summary>
public enum NetDeliveryMethod : byte
{
//
// Actually a publicly visible subset of NetMessageType
//
/// <summary>
/// Indicates an error
/// </summary>
Unknown = 0,
/// <summary>
/// Unreliable, unordered delivery
/// </summary>
Unreliable = 1,
/// <summary>
/// Unreliable delivery, but automatically dropping late messages
/// </summary>
UnreliableSequenced = 2,
/// <summary>
/// Reliable delivery, but unordered
/// </summary>
ReliableUnordered = 34,
/// <summary>
/// Reliable delivery, except for late messages which are dropped
/// </summary>
ReliableSequenced = 35,
/// <summary>
/// Reliable, ordered delivery
/// </summary>
ReliableOrdered = 67,
}
}

View File

@@ -1,83 +1,83 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Runtime.Serialization;
namespace Lidgren.Network
{
/// <summary>
/// Exception thrown in the Lidgren Network Library
/// </summary>
[Serializable]
public sealed class NetException : Exception
{
/// <summary>
/// NetException constructor
/// </summary>
public NetException()
: base()
{
}
/// <summary>
/// NetException constructor
/// </summary>
public NetException(string message)
: base(message)
{
}
/// <summary>
/// NetException constructor
/// </summary>
public NetException(string message, Exception inner)
: base(message, inner)
{
}
/// <summary>
/// NetException constructor
/// </summary>
private NetException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// <summary>
/// Throws an exception, in DEBUG only, if first parameter is false
/// </summary>
[Conditional("DEBUG")]
public static void Assert(bool isOk, string message)
{
if (!isOk)
throw new NetException(message);
}
/// <summary>
/// Throws an exception, in DEBUG only, if first parameter is false
/// </summary>
[Conditional("DEBUG")]
public static void Assert(bool isOk)
{
if (!isOk)
throw new NetException();
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Runtime.Serialization;
namespace Lidgren.Network
{
/// <summary>
/// Exception thrown in the Lidgren Network Library
/// </summary>
[Serializable]
public sealed class NetException : Exception
{
/// <summary>
/// NetException constructor
/// </summary>
public NetException()
: base()
{
}
/// <summary>
/// NetException constructor
/// </summary>
public NetException(string message)
: base(message)
{
}
/// <summary>
/// NetException constructor
/// </summary>
public NetException(string message, Exception inner)
: base(message, inner)
{
}
/// <summary>
/// NetException constructor
/// </summary>
private NetException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// <summary>
/// Throws an exception, in DEBUG only, if first parameter is false
/// </summary>
[Conditional("DEBUG")]
public static void Assert(bool isOk, string message)
{
if (!isOk)
throw new NetException(message);
}
/// <summary>
/// Throws an exception, in DEBUG only, if first parameter is false
/// </summary>
[Conditional("DEBUG")]
public static void Assert(bool isOk)
{
if (!isOk)
throw new NetException();
}
}
}

View File

@@ -1,174 +1,174 @@
using System;
namespace Lidgren.Network
{
internal static class NetFragmentationHelper
{
internal static int WriteHeader(
byte[] destination,
int ptr,
int group,
int totalBits,
int chunkByteSize,
int chunkNumber)
{
uint num1 = (uint)group;
while (num1 >= 0x80)
{
destination[ptr++] = (byte)(num1 | 0x80);
num1 = num1 >> 7;
}
destination[ptr++] = (byte)num1;
// write variable length fragment total bits
uint num2 = (uint)totalBits;
while (num2 >= 0x80)
{
destination[ptr++] = (byte)(num2 | 0x80);
num2 = num2 >> 7;
}
destination[ptr++] = (byte)num2;
// write variable length fragment chunk size
uint num3 = (uint)chunkByteSize;
while (num3 >= 0x80)
{
destination[ptr++] = (byte)(num3 | 0x80);
num3 = num3 >> 7;
}
destination[ptr++] = (byte)num3;
// write variable length fragment chunk number
uint num4 = (uint)chunkNumber;
while (num4 >= 0x80)
{
destination[ptr++] = (byte)(num4 | 0x80);
num4 = num4 >> 7;
}
destination[ptr++] = (byte)num4;
return ptr;
}
internal static int ReadHeader(byte[] buffer, int ptr, out int group, out int totalBits, out int chunkByteSize, out int chunkNumber)
{
int num1 = 0;
int num2 = 0;
while (true)
{
byte num3 = buffer[ptr++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
num2 += 7;
if ((num3 & 0x80) == 0)
{
group = num1;
break;
}
}
num1 = 0;
num2 = 0;
while (true)
{
byte num3 = buffer[ptr++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
num2 += 7;
if ((num3 & 0x80) == 0)
{
totalBits = num1;
break;
}
}
num1 = 0;
num2 = 0;
while (true)
{
byte num3 = buffer[ptr++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
num2 += 7;
if ((num3 & 0x80) == 0)
{
chunkByteSize = num1;
break;
}
}
num1 = 0;
num2 = 0;
while (true)
{
byte num3 = buffer[ptr++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
num2 += 7;
if ((num3 & 0x80) == 0)
{
chunkNumber = num1;
break;
}
}
return ptr;
}
internal static int GetFragmentationHeaderSize(int groupId, int totalBytes, int chunkByteSize, int numChunks)
{
int len = 4;
// write variable length fragment group id
uint num1 = (uint)groupId;
while (num1 >= 0x80)
{
len++;
num1 = num1 >> 7;
}
// write variable length fragment total bits
uint num2 = (uint)(totalBytes * 8);
while (num2 >= 0x80)
{
len++;
num2 = num2 >> 7;
}
// write variable length fragment chunk byte size
uint num3 = (uint)chunkByteSize;
while (num3 >= 0x80)
{
len++;
num3 = num3 >> 7;
}
// write variable length fragment chunk number
uint num4 = (uint)numChunks;
while (num4 >= 0x80)
{
len++;
num4 = num4 >> 7;
}
return len;
}
internal static int GetBestChunkSize(int group, int totalBytes, int mtu)
{
int tryNumChunks = (totalBytes / (mtu - 8)) + 1;
int tryChunkSize = (totalBytes / tryNumChunks) + 1; // +1 since we immediately decrement it in the loop
int headerSize = 0;
do
{
tryChunkSize--; // keep reducing chunk size until it fits within MTU including header
int numChunks = totalBytes / tryChunkSize;
if (numChunks * tryChunkSize < totalBytes)
numChunks++;
headerSize = GetFragmentationHeaderSize(group, totalBytes, tryChunkSize, numChunks);
} while (tryChunkSize + headerSize + 5 + 1 >= mtu);
return tryChunkSize;
}
}
}
using System;
namespace Lidgren.Network
{
internal static class NetFragmentationHelper
{
internal static int WriteHeader(
byte[] destination,
int ptr,
int group,
int totalBits,
int chunkByteSize,
int chunkNumber)
{
uint num1 = (uint)group;
while (num1 >= 0x80)
{
destination[ptr++] = (byte)(num1 | 0x80);
num1 = num1 >> 7;
}
destination[ptr++] = (byte)num1;
// write variable length fragment total bits
uint num2 = (uint)totalBits;
while (num2 >= 0x80)
{
destination[ptr++] = (byte)(num2 | 0x80);
num2 = num2 >> 7;
}
destination[ptr++] = (byte)num2;
// write variable length fragment chunk size
uint num3 = (uint)chunkByteSize;
while (num3 >= 0x80)
{
destination[ptr++] = (byte)(num3 | 0x80);
num3 = num3 >> 7;
}
destination[ptr++] = (byte)num3;
// write variable length fragment chunk number
uint num4 = (uint)chunkNumber;
while (num4 >= 0x80)
{
destination[ptr++] = (byte)(num4 | 0x80);
num4 = num4 >> 7;
}
destination[ptr++] = (byte)num4;
return ptr;
}
internal static int ReadHeader(byte[] buffer, int ptr, out int group, out int totalBits, out int chunkByteSize, out int chunkNumber)
{
int num1 = 0;
int num2 = 0;
while (true)
{
byte num3 = buffer[ptr++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
num2 += 7;
if ((num3 & 0x80) == 0)
{
group = num1;
break;
}
}
num1 = 0;
num2 = 0;
while (true)
{
byte num3 = buffer[ptr++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
num2 += 7;
if ((num3 & 0x80) == 0)
{
totalBits = num1;
break;
}
}
num1 = 0;
num2 = 0;
while (true)
{
byte num3 = buffer[ptr++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
num2 += 7;
if ((num3 & 0x80) == 0)
{
chunkByteSize = num1;
break;
}
}
num1 = 0;
num2 = 0;
while (true)
{
byte num3 = buffer[ptr++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
num2 += 7;
if ((num3 & 0x80) == 0)
{
chunkNumber = num1;
break;
}
}
return ptr;
}
internal static int GetFragmentationHeaderSize(int groupId, int totalBytes, int chunkByteSize, int numChunks)
{
int len = 4;
// write variable length fragment group id
uint num1 = (uint)groupId;
while (num1 >= 0x80)
{
len++;
num1 = num1 >> 7;
}
// write variable length fragment total bits
uint num2 = (uint)(totalBytes * 8);
while (num2 >= 0x80)
{
len++;
num2 = num2 >> 7;
}
// write variable length fragment chunk byte size
uint num3 = (uint)chunkByteSize;
while (num3 >= 0x80)
{
len++;
num3 = num3 >> 7;
}
// write variable length fragment chunk number
uint num4 = (uint)numChunks;
while (num4 >= 0x80)
{
len++;
num4 = num4 >> 7;
}
return len;
}
internal static int GetBestChunkSize(int group, int totalBytes, int mtu)
{
int tryNumChunks = (totalBytes / (mtu - 8)) + 1;
int tryChunkSize = (totalBytes / tryNumChunks) + 1; // +1 since we immediately decrement it in the loop
int headerSize = 0;
do
{
tryChunkSize--; // keep reducing chunk size until it fits within MTU including header
int numChunks = totalBytes / tryChunkSize;
if (numChunks * tryChunkSize < totalBytes)
numChunks++;
headerSize = GetFragmentationHeaderSize(group, totalBytes, tryChunkSize, numChunks);
} while (tryChunkSize + headerSize + 5 + 1 >= mtu);
return tryChunkSize;
}
}
}

View File

@@ -1,12 +1,12 @@
using System;
namespace Lidgren.Network
{
public sealed class NetFragmentationInfo
{
public int TotalFragmentCount;
public bool[] Received;
public int TotalReceived;
public int FragmentSize;
}
}
using System;
namespace Lidgren.Network
{
public sealed class NetFragmentationInfo
{
public int TotalFragmentCount;
public bool[] Received;
public int TotalReceived;
public int FragmentSize;
}
}

View File

@@ -1,314 +1,314 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Net;
namespace Lidgren.Network
{
public partial class NetIncomingMessage
{
/// <summary>
/// Returns the internal data buffer, don't modify
/// </summary>
public byte[] PeekDataBuffer()
{
return m_data;
}
//
// 1 bit
//
/// <summary>
/// Reads a 1-bit Boolean without advancing the read pointer
/// </summary>
public bool PeekBoolean()
{
NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition);
return (retval > 0 ? true : false);
}
//
// 8 bit
//
/// <summary>
/// Reads a Byte without advancing the read pointer
/// </summary>
public byte PeekByte()
{
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
return retval;
}
/// <summary>
/// Reads an SByte without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public sbyte PeekSByte()
{
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
return (sbyte)retval;
}
/// <summary>
/// Reads the specified number of bits into a Byte without advancing the read pointer
/// </summary>
public byte PeekByte(int numberOfBits)
{
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
return retval;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public byte[] PeekBytes(int numberOfBytes)
{
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
byte[] retval = new byte[numberOfBytes];
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0);
return retval;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public void PeekBytes(byte[] into, int offset, int numberOfBytes)
{
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
NetException.Assert(offset + numberOfBytes <= into.Length);
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset);
return;
}
//
// 16 bit
//
/// <summary>
/// Reads an Int16 without advancing the read pointer
/// </summary>
public Int16 PeekInt16()
{
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
return (short)retval;
}
/// <summary>
/// Reads a UInt16 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt16 PeekUInt16()
{
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
return (ushort)retval;
}
//
// 32 bit
//
/// <summary>
/// Reads an Int32 without advancing the read pointer
/// </summary>
public Int32 PeekInt32()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
return (Int32)retval;
}
/// <summary>
/// Reads the specified number of bits into an Int32 without advancing the read pointer
/// </summary>
public Int32 PeekInt32(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadInt() can only read between 1 and 32 bits");
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
if (numberOfBits == 32)
return (int)retval;
int signBit = 1 << (numberOfBits - 1);
if ((retval & signBit) == 0)
return (int)retval; // positive
// negative
unchecked
{
uint mask = ((uint)-1) >> (33 - numberOfBits);
uint tmp = (retval & mask) + 1;
return -((int)tmp);
}
}
/// <summary>
/// Reads a UInt32 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt32 PeekUInt32()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
return retval;
}
/// <summary>
/// Reads the specified number of bits into a UInt32 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt32 PeekUInt32(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadUInt() can only read between 1 and 32 bits");
//NetException.Assert(m_bitLength - m_readBitPtr >= numberOfBits, "tried to read past buffer size");
UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
return retval;
}
//
// 64 bit
//
/// <summary>
/// Reads a UInt64 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt64 PeekUInt64()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition + 32);
ulong retval = low + (high << 32);
return retval;
}
/// <summary>
/// Reads an Int64 without advancing the read pointer
/// </summary>
public Int64 PeekInt64()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
unchecked
{
ulong retval = PeekUInt64();
long longRetval = (long)retval;
return longRetval;
}
}
/// <summary>
/// Reads the specified number of bits into an UInt64 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt64 PeekUInt64(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 64), "ReadUInt() can only read between 1 and 64 bits");
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
ulong retval;
if (numberOfBits <= 32)
{
retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
}
else
{
retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32;
}
return retval;
}
/// <summary>
/// Reads the specified number of bits into an Int64 without advancing the read pointer
/// </summary>
public Int64 PeekInt64(int numberOfBits)
{
NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits");
return (long)PeekUInt64(numberOfBits);
}
//
// Floating point
//
/// <summary>
/// Reads a 32-bit Single without advancing the read pointer
/// </summary>
public float PeekFloat()
{
return PeekSingle();
}
/// <summary>
/// Reads a 32-bit Single without advancing the read pointer
/// </summary>
public float PeekSingle()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
if ((m_readPosition & 7) == 0) // read directly
{
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
return retval;
}
byte[] bytes = PeekBytes(4);
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
/// Reads a 64-bit Double without advancing the read pointer
/// </summary>
public double PeekDouble()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
if ((m_readPosition & 7) == 0) // read directly
{
// read directly
double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3);
return retval;
}
byte[] bytes = PeekBytes(8);
return BitConverter.ToDouble(bytes, 0);
}
/// <summary>
/// Reads a string without advancing the read pointer
/// </summary>
public string PeekString()
{
int wasReadPosition = m_readPosition;
string retval = ReadString();
m_readPosition = wasReadPosition;
return retval;
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Net;
namespace Lidgren.Network
{
public partial class NetIncomingMessage
{
/// <summary>
/// Returns the internal data buffer, don't modify
/// </summary>
public byte[] PeekDataBuffer()
{
return m_data;
}
//
// 1 bit
//
/// <summary>
/// Reads a 1-bit Boolean without advancing the read pointer
/// </summary>
public bool PeekBoolean()
{
NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition);
return (retval > 0 ? true : false);
}
//
// 8 bit
//
/// <summary>
/// Reads a Byte without advancing the read pointer
/// </summary>
public byte PeekByte()
{
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
return retval;
}
/// <summary>
/// Reads an SByte without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public sbyte PeekSByte()
{
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
return (sbyte)retval;
}
/// <summary>
/// Reads the specified number of bits into a Byte without advancing the read pointer
/// </summary>
public byte PeekByte(int numberOfBits)
{
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
return retval;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public byte[] PeekBytes(int numberOfBytes)
{
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
byte[] retval = new byte[numberOfBytes];
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0);
return retval;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public void PeekBytes(byte[] into, int offset, int numberOfBytes)
{
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
NetException.Assert(offset + numberOfBytes <= into.Length);
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset);
return;
}
//
// 16 bit
//
/// <summary>
/// Reads an Int16 without advancing the read pointer
/// </summary>
public Int16 PeekInt16()
{
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
return (short)retval;
}
/// <summary>
/// Reads a UInt16 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt16 PeekUInt16()
{
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
return (ushort)retval;
}
//
// 32 bit
//
/// <summary>
/// Reads an Int32 without advancing the read pointer
/// </summary>
public Int32 PeekInt32()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
return (Int32)retval;
}
/// <summary>
/// Reads the specified number of bits into an Int32 without advancing the read pointer
/// </summary>
public Int32 PeekInt32(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadInt() can only read between 1 and 32 bits");
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
if (numberOfBits == 32)
return (int)retval;
int signBit = 1 << (numberOfBits - 1);
if ((retval & signBit) == 0)
return (int)retval; // positive
// negative
unchecked
{
uint mask = ((uint)-1) >> (33 - numberOfBits);
uint tmp = (retval & mask) + 1;
return -((int)tmp);
}
}
/// <summary>
/// Reads a UInt32 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt32 PeekUInt32()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
return retval;
}
/// <summary>
/// Reads the specified number of bits into a UInt32 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt32 PeekUInt32(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadUInt() can only read between 1 and 32 bits");
//NetException.Assert(m_bitLength - m_readBitPtr >= numberOfBits, "tried to read past buffer size");
UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
return retval;
}
//
// 64 bit
//
/// <summary>
/// Reads a UInt64 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt64 PeekUInt64()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition + 32);
ulong retval = low + (high << 32);
return retval;
}
/// <summary>
/// Reads an Int64 without advancing the read pointer
/// </summary>
public Int64 PeekInt64()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
unchecked
{
ulong retval = PeekUInt64();
long longRetval = (long)retval;
return longRetval;
}
}
/// <summary>
/// Reads the specified number of bits into an UInt64 without advancing the read pointer
/// </summary>
[CLSCompliant(false)]
public UInt64 PeekUInt64(int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 64), "ReadUInt() can only read between 1 and 64 bits");
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
ulong retval;
if (numberOfBits <= 32)
{
retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
}
else
{
retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32;
}
return retval;
}
/// <summary>
/// Reads the specified number of bits into an Int64 without advancing the read pointer
/// </summary>
public Int64 PeekInt64(int numberOfBits)
{
NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits");
return (long)PeekUInt64(numberOfBits);
}
//
// Floating point
//
/// <summary>
/// Reads a 32-bit Single without advancing the read pointer
/// </summary>
public float PeekFloat()
{
return PeekSingle();
}
/// <summary>
/// Reads a 32-bit Single without advancing the read pointer
/// </summary>
public float PeekSingle()
{
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
if ((m_readPosition & 7) == 0) // read directly
{
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
return retval;
}
byte[] bytes = PeekBytes(4);
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
/// Reads a 64-bit Double without advancing the read pointer
/// </summary>
public double PeekDouble()
{
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
if ((m_readPosition & 7) == 0) // read directly
{
// read directly
double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3);
return retval;
}
byte[] bytes = PeekBytes(8);
return BitConverter.ToDouble(bytes, 0);
}
/// <summary>
/// Reads a string without advancing the read pointer
/// </summary>
public string PeekString()
{
int wasReadPosition = m_readPosition;
string retval = ReadString();
m_readPosition = wasReadPosition;
return retval;
}
}
}

View File

@@ -1,103 +1,103 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetIncomingMessage
{
/// <summary>
/// Reads all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void ReadAllFields(object target)
{
ReadAllFields(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Reads all fields with the specified binding of the object in alphabetical order using reflection
/// </summary>
public void ReadAllFields(object target, BindingFlags flags)
{
if (target == null)
return;
Type tp = target.GetType();
FieldInfo[] fields = tp.GetFields(flags);
NetUtility.SortMembersList(fields);
foreach (FieldInfo fi in fields)
{
object value;
// find read method
MethodInfo readMethod;
if (s_readMethods.TryGetValue(fi.FieldType, out readMethod))
{
// read value
value = readMethod.Invoke(this, null);
// set the value
fi.SetValue(target, value);
}
}
}
/// <summary>
/// Reads all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void ReadAllProperties(object target)
{
ReadAllProperties(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Reads all fields with the specified binding of the object in alphabetical order using reflection
/// </summary>
public void ReadAllProperties(object target, BindingFlags flags)
{
if (target == null)
throw new ArgumentNullException("target");
if (target == null)
return;
Type tp = target.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
NetUtility.SortMembersList(fields);
foreach (PropertyInfo fi in fields)
{
object value;
// find read method
MethodInfo readMethod;
if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod))
{
// read value
value = readMethod.Invoke(this, null);
// set the value
MethodInfo setMethod = fi.GetSetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
setMethod.Invoke(target, new object[] { value });
}
}
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetIncomingMessage
{
/// <summary>
/// Reads all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void ReadAllFields(object target)
{
ReadAllFields(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Reads all fields with the specified binding of the object in alphabetical order using reflection
/// </summary>
public void ReadAllFields(object target, BindingFlags flags)
{
if (target == null)
return;
Type tp = target.GetType();
FieldInfo[] fields = tp.GetFields(flags);
NetUtility.SortMembersList(fields);
foreach (FieldInfo fi in fields)
{
object value;
// find read method
MethodInfo readMethod;
if (s_readMethods.TryGetValue(fi.FieldType, out readMethod))
{
// read value
value = readMethod.Invoke(this, null);
// set the value
fi.SetValue(target, value);
}
}
}
/// <summary>
/// Reads all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void ReadAllProperties(object target)
{
ReadAllProperties(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Reads all fields with the specified binding of the object in alphabetical order using reflection
/// </summary>
public void ReadAllProperties(object target, BindingFlags flags)
{
if (target == null)
throw new ArgumentNullException("target");
if (target == null)
return;
Type tp = target.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
NetUtility.SortMembersList(fields);
foreach (PropertyInfo fi in fields)
{
object value;
// find read method
MethodInfo readMethod;
if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod))
{
// read value
value = readMethod.Invoke(this, null);
// set the value
MethodInfo setMethod = fi.GetSetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
setMethod.Invoke(target, new object[] { value });
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,475 +1,475 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using System.Diagnostics;
using System.Text;
namespace Lidgren.Network
{
public partial class NetIncomingMessage
{
/// <summary>
/// Ensures the buffer can hold this number of bits
/// </summary>
private void InternalEnsureBufferSize(int numberOfBits)
{
int byteLen = ((numberOfBits + 7) >> 3);
if (m_data == null)
{
m_data = new byte[byteLen];
return;
}
if (m_data.Length < byteLen)
Array.Resize<byte>(ref m_data, byteLen);
return;
}
//
// 1 bit
//
internal void Write(bool value)
{
InternalEnsureBufferSize(m_bitLength + 1);
NetBitWriter.WriteByte((value ? (byte)1 : (byte)0), 1, m_data, m_bitLength);
m_bitLength += 1;
}
//
// 8 bit
//
internal void Write(byte source)
{
InternalEnsureBufferSize(m_bitLength + 8);
NetBitWriter.WriteByte(source, 8, m_data, m_bitLength);
m_bitLength += 8;
}
internal void Write(sbyte source)
{
InternalEnsureBufferSize(m_bitLength + 8);
NetBitWriter.WriteByte((byte)source, 8, m_data, m_bitLength);
m_bitLength += 8;
}
internal void Write(byte source, int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 8), "Write(byte, numberOfBits) can only write between 1 and 8 bits");
InternalEnsureBufferSize(m_bitLength + numberOfBits);
NetBitWriter.WriteByte(source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
internal void Write(byte[] source)
{
if (source == null)
throw new ArgumentNullException("source");
int bits = source.Length * 8;
InternalEnsureBufferSize(m_bitLength + bits);
NetBitWriter.WriteBytes(source, 0, source.Length, m_data, m_bitLength);
m_bitLength += bits;
}
internal void Write(byte[] source, int offsetInBytes, int numberOfBytes)
{
if (source == null)
throw new ArgumentNullException("source");
int bits = numberOfBytes * 8;
InternalEnsureBufferSize(m_bitLength + bits);
NetBitWriter.WriteBytes(source, offsetInBytes, numberOfBytes, m_data, m_bitLength);
m_bitLength += bits;
}
//
// 16 bit
//
internal void Write(UInt16 source)
{
InternalEnsureBufferSize(m_bitLength + 16);
NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength);
m_bitLength += 16;
}
internal void Write(UInt16 source, int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 16), "Write(ushort, numberOfBits) can only write between 1 and 16 bits");
InternalEnsureBufferSize(m_bitLength + numberOfBits);
NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
internal void Write(Int16 source)
{
InternalEnsureBufferSize(m_bitLength + 16);
NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength);
m_bitLength += 16;
}
//
// 32 bit
//
#if UNSAFE
internal unsafe void Write(Int32 source)
{
EnsureBufferSize(m_bitLength + 32);
// can write fast?
if (m_bitLength % 8 == 0)
{
fixed (byte* numRef = &Data[m_bitLength / 8])
{
*((int*)numRef) = source;
}
}
else
{
NetBitWriter.WriteUInt32((UInt32)source, 32, Data, m_bitLength);
}
m_bitLength += 32;
}
#else
internal void Write(Int32 source)
{
InternalEnsureBufferSize(m_bitLength + 32);
NetBitWriter.WriteUInt32((UInt32)source, 32, m_data, m_bitLength);
m_bitLength += 32;
}
#endif
#if UNSAFE
internal unsafe void Write(UInt32 source)
{
EnsureBufferSize(m_bitLength + 32);
// can write fast?
if (m_bitLength % 8 == 0)
{
fixed (byte* numRef = &Data[m_bitLength / 8])
{
*((uint*)numRef) = source;
}
}
else
{
NetBitWriter.WriteUInt32(source, 32, Data, m_bitLength);
}
m_bitLength += 32;
}
#else
internal void Write(UInt32 source)
{
InternalEnsureBufferSize(m_bitLength + 32);
NetBitWriter.WriteUInt32(source, 32, m_data, m_bitLength);
m_bitLength += 32;
}
#endif
internal void Write(UInt32 source, int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(uint, numberOfBits) can only write between 1 and 32 bits");
InternalEnsureBufferSize(m_bitLength + numberOfBits);
NetBitWriter.WriteUInt32(source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
internal void Write(Int32 source, int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(int, numberOfBits) can only write between 1 and 32 bits");
InternalEnsureBufferSize(m_bitLength + numberOfBits);
if (numberOfBits != 32)
{
// make first bit sign
int signBit = 1 << (numberOfBits - 1);
if (source < 0)
source = (-source - 1) | signBit;
else
source &= (~signBit);
}
NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
//
// 64 bit
//
internal void Write(UInt64 source)
{
InternalEnsureBufferSize(m_bitLength + 64);
NetBitWriter.WriteUInt64(source, 64, m_data, m_bitLength);
m_bitLength += 64;
}
internal void Write(UInt64 source, int numberOfBits)
{
InternalEnsureBufferSize(m_bitLength + numberOfBits);
NetBitWriter.WriteUInt64(source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
internal void Write(Int64 source)
{
InternalEnsureBufferSize(m_bitLength + 64);
ulong usource = (ulong)source;
NetBitWriter.WriteUInt64(usource, 64, m_data, m_bitLength);
m_bitLength += 64;
}
internal void Write(Int64 source, int numberOfBits)
{
InternalEnsureBufferSize(m_bitLength + numberOfBits);
ulong usource = (ulong)source;
NetBitWriter.WriteUInt64(usource, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
//
// Floating point
//
#if UNSAFE
internal unsafe void Write(float source)
{
uint val = *((uint*)&source);
#if BIGENDIAN
val = NetUtility.SwapByteOrder(val);
#endif
Write(val);
}
#else
internal void Write(float source)
{
byte[] val = BitConverter.GetBytes(source);
#if BIGENDIAN
// swap byte order
byte tmp = val[3];
val[3] = val[0];
val[0] = tmp;
tmp = val[2];
val[2] = val[1];
val[1] = tmp;
#endif
Write(val);
}
#endif
#if UNSAFE
internal unsafe void Write(double source)
{
ulong val = *((ulong*)&source);
#if BIGENDIAN
val = NetUtility.SwapByteOrder(val);
#endif
Write(val);
}
#else
internal void Write(double source)
{
byte[] val = BitConverter.GetBytes(source);
#if BIGENDIAN
// 0 1 2 3 4 5 6 7
// swap byte order
byte tmp = val[7];
val[7] = val[0];
val[0] = tmp;
tmp = val[6];
val[6] = val[1];
val[1] = tmp;
tmp = val[5];
val[5] = val[2];
val[2] = tmp;
tmp = val[4];
val[4] = val[3];
val[3] = tmp;
#endif
Write(val);
}
#endif
//
// Variable bits
//
/// <summary>
/// Write Base128 encoded variable sized unsigned integer
/// </summary>
/// <returns>number of bytes written</returns>
internal int WriteVariableUInt32(uint value)
{
int retval = 1;
uint num1 = (uint)value;
while (num1 >= 0x80)
{
this.Write((byte)(num1 | 0x80));
num1 = num1 >> 7;
retval++;
}
this.Write((byte)num1);
return retval;
}
/// <summary>
/// Write Base128 encoded variable sized signed integer
/// </summary>
/// <returns>number of bytes written</returns>
internal int WriteVariableInt32(int value)
{
int retval = 1;
uint num1 = (uint)((value << 1) ^ (value >> 31));
while (num1 >= 0x80)
{
this.Write((byte)(num1 | 0x80));
num1 = num1 >> 7;
retval++;
}
this.Write((byte)num1);
return retval;
}
/// <summary>
/// Write Base128 encoded variable sized unsigned integer
/// </summary>
/// <returns>number of bytes written</returns>
internal int WriteVariableUInt64(UInt64 value)
{
int retval = 1;
UInt64 num1 = (UInt64)value;
while (num1 >= 0x80)
{
this.Write((byte)(num1 | 0x80));
num1 = num1 >> 7;
retval++;
}
this.Write((byte)num1);
return retval;
}
/// <summary>
/// Compress (lossy) a float in the range -1..1 using numberOfBits bits
/// </summary>
internal void WriteSignedSingle(float value, int numberOfBits)
{
NetException.Assert(((value >= -1.0) && (value <= 1.0)), " WriteSignedSingle() must be passed a float in the range -1 to 1; val is " + value);
float unit = (value + 1.0f) * 0.5f;
int maxVal = (1 << numberOfBits) - 1;
uint writeVal = (uint)(unit * (float)maxVal);
Write(writeVal, numberOfBits);
}
/// <summary>
/// Compress (lossy) a float in the range 0..1 using numberOfBits bits
/// </summary>
internal void WriteUnitSingle(float value, int numberOfBits)
{
NetException.Assert(((value >= 0.0) && (value <= 1.0)), " WriteUnitSingle() must be passed a float in the range 0 to 1; val is " + value);
int maxValue = (1 << numberOfBits) - 1;
uint writeVal = (uint)(value * (float)maxValue);
Write(writeVal, numberOfBits);
}
/// <summary>
/// Compress a float within a specified range using a certain number of bits
/// </summary>
internal void WriteRangedSingle(float value, float min, float max, int numberOfBits)
{
NetException.Assert(((value >= min) && (value <= max)), " WriteRangedSingle() must be passed a float in the range MIN to MAX; val is " + value);
float range = max - min;
float unit = ((value - min) / range);
int maxVal = (1 << numberOfBits) - 1;
Write((UInt32)((float)maxVal * unit), numberOfBits);
}
/// <summary>
/// Writes an integer with the least amount of bits need for the specified range
/// Returns number of bits written
/// </summary>
internal int WriteRangedInteger(int min, int max, int value)
{
NetException.Assert(value >= min && value <= max, "Value not within min/max range!");
uint range = (uint)(max - min);
int numBits = NetUtility.BitsToHoldUInt(range);
uint rvalue = (uint)(value - min);
Write(rvalue, numBits);
return numBits;
}
/// <summary>
/// Write a string
/// </summary>
internal void Write(string source)
{
if (string.IsNullOrEmpty(source))
{
InternalEnsureBufferSize(m_bitLength + 8);
WriteVariableUInt32(0);
return;
}
byte[] bytes = Encoding.UTF8.GetBytes(source);
InternalEnsureBufferSize(m_bitLength + ((bytes.Length + 2) * 8));
WriteVariableUInt32((uint)bytes.Length);
Write(bytes);
}
/// <summary>
/// Writes an endpoint description
/// </summary>
internal void Write(IPEndPoint endPoint)
{
byte[] bytes = endPoint.Address.GetAddressBytes();
Write((byte)bytes.Length);
Write(bytes);
Write((ushort)endPoint.Port);
}
/// <summary>
/// Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes.
/// </summary>
internal void WritePadBits()
{
m_bitLength = ((m_bitLength + 7) / 8) * 8;
InternalEnsureBufferSize(m_bitLength);
}
/// <summary>
/// Pads data with the specified number of bits.
/// </summary>
internal void WritePadBits(int numberOfBits)
{
m_bitLength += numberOfBits;
InternalEnsureBufferSize(m_bitLength);
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using System.Diagnostics;
using System.Text;
namespace Lidgren.Network
{
public partial class NetIncomingMessage
{
/// <summary>
/// Ensures the buffer can hold this number of bits
/// </summary>
private void InternalEnsureBufferSize(int numberOfBits)
{
int byteLen = ((numberOfBits + 7) >> 3);
if (m_data == null)
{
m_data = new byte[byteLen];
return;
}
if (m_data.Length < byteLen)
Array.Resize<byte>(ref m_data, byteLen);
return;
}
//
// 1 bit
//
internal void Write(bool value)
{
InternalEnsureBufferSize(m_bitLength + 1);
NetBitWriter.WriteByte((value ? (byte)1 : (byte)0), 1, m_data, m_bitLength);
m_bitLength += 1;
}
//
// 8 bit
//
internal void Write(byte source)
{
InternalEnsureBufferSize(m_bitLength + 8);
NetBitWriter.WriteByte(source, 8, m_data, m_bitLength);
m_bitLength += 8;
}
internal void Write(sbyte source)
{
InternalEnsureBufferSize(m_bitLength + 8);
NetBitWriter.WriteByte((byte)source, 8, m_data, m_bitLength);
m_bitLength += 8;
}
internal void Write(byte source, int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 8), "Write(byte, numberOfBits) can only write between 1 and 8 bits");
InternalEnsureBufferSize(m_bitLength + numberOfBits);
NetBitWriter.WriteByte(source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
internal void Write(byte[] source)
{
if (source == null)
throw new ArgumentNullException("source");
int bits = source.Length * 8;
InternalEnsureBufferSize(m_bitLength + bits);
NetBitWriter.WriteBytes(source, 0, source.Length, m_data, m_bitLength);
m_bitLength += bits;
}
internal void Write(byte[] source, int offsetInBytes, int numberOfBytes)
{
if (source == null)
throw new ArgumentNullException("source");
int bits = numberOfBytes * 8;
InternalEnsureBufferSize(m_bitLength + bits);
NetBitWriter.WriteBytes(source, offsetInBytes, numberOfBytes, m_data, m_bitLength);
m_bitLength += bits;
}
//
// 16 bit
//
internal void Write(UInt16 source)
{
InternalEnsureBufferSize(m_bitLength + 16);
NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength);
m_bitLength += 16;
}
internal void Write(UInt16 source, int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 16), "Write(ushort, numberOfBits) can only write between 1 and 16 bits");
InternalEnsureBufferSize(m_bitLength + numberOfBits);
NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
internal void Write(Int16 source)
{
InternalEnsureBufferSize(m_bitLength + 16);
NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength);
m_bitLength += 16;
}
//
// 32 bit
//
#if UNSAFE
internal unsafe void Write(Int32 source)
{
EnsureBufferSize(m_bitLength + 32);
// can write fast?
if (m_bitLength % 8 == 0)
{
fixed (byte* numRef = &Data[m_bitLength / 8])
{
*((int*)numRef) = source;
}
}
else
{
NetBitWriter.WriteUInt32((UInt32)source, 32, Data, m_bitLength);
}
m_bitLength += 32;
}
#else
internal void Write(Int32 source)
{
InternalEnsureBufferSize(m_bitLength + 32);
NetBitWriter.WriteUInt32((UInt32)source, 32, m_data, m_bitLength);
m_bitLength += 32;
}
#endif
#if UNSAFE
internal unsafe void Write(UInt32 source)
{
EnsureBufferSize(m_bitLength + 32);
// can write fast?
if (m_bitLength % 8 == 0)
{
fixed (byte* numRef = &Data[m_bitLength / 8])
{
*((uint*)numRef) = source;
}
}
else
{
NetBitWriter.WriteUInt32(source, 32, Data, m_bitLength);
}
m_bitLength += 32;
}
#else
internal void Write(UInt32 source)
{
InternalEnsureBufferSize(m_bitLength + 32);
NetBitWriter.WriteUInt32(source, 32, m_data, m_bitLength);
m_bitLength += 32;
}
#endif
internal void Write(UInt32 source, int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(uint, numberOfBits) can only write between 1 and 32 bits");
InternalEnsureBufferSize(m_bitLength + numberOfBits);
NetBitWriter.WriteUInt32(source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
internal void Write(Int32 source, int numberOfBits)
{
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(int, numberOfBits) can only write between 1 and 32 bits");
InternalEnsureBufferSize(m_bitLength + numberOfBits);
if (numberOfBits != 32)
{
// make first bit sign
int signBit = 1 << (numberOfBits - 1);
if (source < 0)
source = (-source - 1) | signBit;
else
source &= (~signBit);
}
NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
//
// 64 bit
//
internal void Write(UInt64 source)
{
InternalEnsureBufferSize(m_bitLength + 64);
NetBitWriter.WriteUInt64(source, 64, m_data, m_bitLength);
m_bitLength += 64;
}
internal void Write(UInt64 source, int numberOfBits)
{
InternalEnsureBufferSize(m_bitLength + numberOfBits);
NetBitWriter.WriteUInt64(source, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
internal void Write(Int64 source)
{
InternalEnsureBufferSize(m_bitLength + 64);
ulong usource = (ulong)source;
NetBitWriter.WriteUInt64(usource, 64, m_data, m_bitLength);
m_bitLength += 64;
}
internal void Write(Int64 source, int numberOfBits)
{
InternalEnsureBufferSize(m_bitLength + numberOfBits);
ulong usource = (ulong)source;
NetBitWriter.WriteUInt64(usource, numberOfBits, m_data, m_bitLength);
m_bitLength += numberOfBits;
}
//
// Floating point
//
#if UNSAFE
internal unsafe void Write(float source)
{
uint val = *((uint*)&source);
#if BIGENDIAN
val = NetUtility.SwapByteOrder(val);
#endif
Write(val);
}
#else
internal void Write(float source)
{
byte[] val = BitConverter.GetBytes(source);
#if BIGENDIAN
// swap byte order
byte tmp = val[3];
val[3] = val[0];
val[0] = tmp;
tmp = val[2];
val[2] = val[1];
val[1] = tmp;
#endif
Write(val);
}
#endif
#if UNSAFE
internal unsafe void Write(double source)
{
ulong val = *((ulong*)&source);
#if BIGENDIAN
val = NetUtility.SwapByteOrder(val);
#endif
Write(val);
}
#else
internal void Write(double source)
{
byte[] val = BitConverter.GetBytes(source);
#if BIGENDIAN
// 0 1 2 3 4 5 6 7
// swap byte order
byte tmp = val[7];
val[7] = val[0];
val[0] = tmp;
tmp = val[6];
val[6] = val[1];
val[1] = tmp;
tmp = val[5];
val[5] = val[2];
val[2] = tmp;
tmp = val[4];
val[4] = val[3];
val[3] = tmp;
#endif
Write(val);
}
#endif
//
// Variable bits
//
/// <summary>
/// Write Base128 encoded variable sized unsigned integer
/// </summary>
/// <returns>number of bytes written</returns>
internal int WriteVariableUInt32(uint value)
{
int retval = 1;
uint num1 = (uint)value;
while (num1 >= 0x80)
{
this.Write((byte)(num1 | 0x80));
num1 = num1 >> 7;
retval++;
}
this.Write((byte)num1);
return retval;
}
/// <summary>
/// Write Base128 encoded variable sized signed integer
/// </summary>
/// <returns>number of bytes written</returns>
internal int WriteVariableInt32(int value)
{
int retval = 1;
uint num1 = (uint)((value << 1) ^ (value >> 31));
while (num1 >= 0x80)
{
this.Write((byte)(num1 | 0x80));
num1 = num1 >> 7;
retval++;
}
this.Write((byte)num1);
return retval;
}
/// <summary>
/// Write Base128 encoded variable sized unsigned integer
/// </summary>
/// <returns>number of bytes written</returns>
internal int WriteVariableUInt64(UInt64 value)
{
int retval = 1;
UInt64 num1 = (UInt64)value;
while (num1 >= 0x80)
{
this.Write((byte)(num1 | 0x80));
num1 = num1 >> 7;
retval++;
}
this.Write((byte)num1);
return retval;
}
/// <summary>
/// Compress (lossy) a float in the range -1..1 using numberOfBits bits
/// </summary>
internal void WriteSignedSingle(float value, int numberOfBits)
{
NetException.Assert(((value >= -1.0) && (value <= 1.0)), " WriteSignedSingle() must be passed a float in the range -1 to 1; val is " + value);
float unit = (value + 1.0f) * 0.5f;
int maxVal = (1 << numberOfBits) - 1;
uint writeVal = (uint)(unit * (float)maxVal);
Write(writeVal, numberOfBits);
}
/// <summary>
/// Compress (lossy) a float in the range 0..1 using numberOfBits bits
/// </summary>
internal void WriteUnitSingle(float value, int numberOfBits)
{
NetException.Assert(((value >= 0.0) && (value <= 1.0)), " WriteUnitSingle() must be passed a float in the range 0 to 1; val is " + value);
int maxValue = (1 << numberOfBits) - 1;
uint writeVal = (uint)(value * (float)maxValue);
Write(writeVal, numberOfBits);
}
/// <summary>
/// Compress a float within a specified range using a certain number of bits
/// </summary>
internal void WriteRangedSingle(float value, float min, float max, int numberOfBits)
{
NetException.Assert(((value >= min) && (value <= max)), " WriteRangedSingle() must be passed a float in the range MIN to MAX; val is " + value);
float range = max - min;
float unit = ((value - min) / range);
int maxVal = (1 << numberOfBits) - 1;
Write((UInt32)((float)maxVal * unit), numberOfBits);
}
/// <summary>
/// Writes an integer with the least amount of bits need for the specified range
/// Returns number of bits written
/// </summary>
internal int WriteRangedInteger(int min, int max, int value)
{
NetException.Assert(value >= min && value <= max, "Value not within min/max range!");
uint range = (uint)(max - min);
int numBits = NetUtility.BitsToHoldUInt(range);
uint rvalue = (uint)(value - min);
Write(rvalue, numBits);
return numBits;
}
/// <summary>
/// Write a string
/// </summary>
internal void Write(string source)
{
if (string.IsNullOrEmpty(source))
{
InternalEnsureBufferSize(m_bitLength + 8);
WriteVariableUInt32(0);
return;
}
byte[] bytes = Encoding.UTF8.GetBytes(source);
InternalEnsureBufferSize(m_bitLength + ((bytes.Length + 2) * 8));
WriteVariableUInt32((uint)bytes.Length);
Write(bytes);
}
/// <summary>
/// Writes an endpoint description
/// </summary>
internal void Write(IPEndPoint endPoint)
{
byte[] bytes = endPoint.Address.GetAddressBytes();
Write((byte)bytes.Length);
Write(bytes);
Write((ushort)endPoint.Port);
}
/// <summary>
/// Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes.
/// </summary>
internal void WritePadBits()
{
m_bitLength = ((m_bitLength + 7) / 8) * 8;
InternalEnsureBufferSize(m_bitLength);
}
/// <summary>
/// Pads data with the specified number of bits.
/// </summary>
internal void WritePadBits(int numberOfBits)
{
m_bitLength += numberOfBits;
InternalEnsureBufferSize(m_bitLength);
}
}
}

View File

@@ -1,115 +1,115 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Net;
using System.Diagnostics;
namespace Lidgren.Network
{
/// <summary>
/// Incoming message either sent from a remote peer or generated within the library
/// </summary>
[DebuggerDisplay("Type={MessageType} LengthBits={LengthBits}")]
public sealed class NetIncomingMessage : NetBuffer
{
internal NetIncomingMessageType m_incomingMessageType;
internal IPEndPoint m_senderEndPoint;
internal NetConnection m_senderConnection;
internal int m_sequenceNumber;
internal NetMessageType m_receivedMessageType;
internal bool m_isFragment;
internal double m_receiveTime;
/// <summary>
/// Gets the type of this incoming message
/// </summary>
public NetIncomingMessageType MessageType { get { return m_incomingMessageType; } }
/// <summary>
/// Gets the delivery method this message was sent with (if user data)
/// </summary>
public NetDeliveryMethod DeliveryMethod { get { return NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
/// <summary>
/// Gets the sequence channel this message was sent with (if user data)
/// </summary>
public int SequenceChannel { get { return (int)m_receivedMessageType - (int)NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
/// <summary>
/// IPEndPoint of sender, if any
/// </summary>
public IPEndPoint SenderEndPoint { get { return m_senderEndPoint; } }
/// <summary>
/// NetConnection of sender, if any
/// </summary>
public NetConnection SenderConnection { get { return m_senderConnection; } }
/// <summary>
/// What local time the message was received from the network
/// </summary>
public double ReceiveTime { get { return m_receiveTime; } }
internal NetIncomingMessage()
{
}
internal NetIncomingMessage(NetIncomingMessageType tp)
{
m_incomingMessageType = tp;
}
internal void Reset()
{
m_incomingMessageType = NetIncomingMessageType.Error;
m_readPosition = 0;
m_receivedMessageType = NetMessageType.LibraryError;
m_senderConnection = null;
m_bitLength = 0;
m_isFragment = false;
}
/// <summary>
/// Decrypt a message
/// </summary>
/// <param name="encryption">The encryption algorithm used to encrypt the message</param>
/// <returns>true on success</returns>
public bool Decrypt(INetEncryption encryption)
{
return encryption.Decrypt(this);
}
/// <summary>
/// Reads a value, in local time comparable to NetTime.Now, written using WriteTime()
/// Must have a connected sender
/// </summary>
public double ReadTime(bool highPrecision)
{
return ReadTime(m_senderConnection, highPrecision);
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
return "[NetIncomingMessage #" + m_sequenceNumber + " " + this.LengthBytes + " bytes]";
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Net;
using System.Diagnostics;
namespace Lidgren.Network
{
/// <summary>
/// Incoming message either sent from a remote peer or generated within the library
/// </summary>
[DebuggerDisplay("Type={MessageType} LengthBits={LengthBits}")]
public sealed class NetIncomingMessage : NetBuffer
{
internal NetIncomingMessageType m_incomingMessageType;
internal IPEndPoint m_senderEndPoint;
internal NetConnection m_senderConnection;
internal int m_sequenceNumber;
internal NetMessageType m_receivedMessageType;
internal bool m_isFragment;
internal double m_receiveTime;
/// <summary>
/// Gets the type of this incoming message
/// </summary>
public NetIncomingMessageType MessageType { get { return m_incomingMessageType; } }
/// <summary>
/// Gets the delivery method this message was sent with (if user data)
/// </summary>
public NetDeliveryMethod DeliveryMethod { get { return NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
/// <summary>
/// Gets the sequence channel this message was sent with (if user data)
/// </summary>
public int SequenceChannel { get { return (int)m_receivedMessageType - (int)NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
/// <summary>
/// IPEndPoint of sender, if any
/// </summary>
public IPEndPoint SenderEndPoint { get { return m_senderEndPoint; } }
/// <summary>
/// NetConnection of sender, if any
/// </summary>
public NetConnection SenderConnection { get { return m_senderConnection; } }
/// <summary>
/// What local time the message was received from the network
/// </summary>
public double ReceiveTime { get { return m_receiveTime; } }
internal NetIncomingMessage()
{
}
internal NetIncomingMessage(NetIncomingMessageType tp)
{
m_incomingMessageType = tp;
}
internal void Reset()
{
m_incomingMessageType = NetIncomingMessageType.Error;
m_readPosition = 0;
m_receivedMessageType = NetMessageType.LibraryError;
m_senderConnection = null;
m_bitLength = 0;
m_isFragment = false;
}
/// <summary>
/// Decrypt a message
/// </summary>
/// <param name="encryption">The encryption algorithm used to encrypt the message</param>
/// <returns>true on success</returns>
public bool Decrypt(INetEncryption encryption)
{
return encryption.Decrypt(this);
}
/// <summary>
/// Reads a value, in local time comparable to NetTime.Now, written using WriteTime()
/// Must have a connected sender
/// </summary>
public double ReadTime(bool highPrecision)
{
return ReadTime(m_senderConnection, highPrecision);
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
return "[NetIncomingMessage #" + m_sequenceNumber + " " + this.LengthBytes + " bytes]";
}
}
}

View File

@@ -1,105 +1,105 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics.CodeAnalysis;
namespace Lidgren.Network
{
/// <summary>
/// The type of a NetIncomingMessage
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
public enum NetIncomingMessageType
{
//
// library note: values are power-of-two, but they are not flags - it's a convenience for NetPeerConfiguration.DisabledMessageTypes
//
/// <summary>
/// Error; this value should never appear
/// </summary>
Error = 0,
/// <summary>
/// Status for a connection changed
/// </summary>
StatusChanged = 1 << 0, // Data (string)
/// <summary>
/// Data sent using SendUnconnectedMessage
/// </summary>
UnconnectedData = 1 << 1, // Data Based on data received
/// <summary>
/// Connection approval is needed
/// </summary>
ConnectionApproval = 1 << 2, // Data
/// <summary>
/// Application data
/// </summary>
Data = 1 << 3, // Data Based on data received
/// <summary>
/// Receipt of delivery
/// </summary>
Receipt = 1 << 4, // Data
/// <summary>
/// Discovery request for a response
/// </summary>
DiscoveryRequest = 1 << 5, // (no data)
/// <summary>
/// Discovery response to a request
/// </summary>
DiscoveryResponse = 1 << 6, // Data
/// <summary>
/// Verbose debug message
/// </summary>
VerboseDebugMessage = 1 << 7, // Data (string)
/// <summary>
/// Debug message
/// </summary>
DebugMessage = 1 << 8, // Data (string)
/// <summary>
/// Warning message
/// </summary>
WarningMessage = 1 << 9, // Data (string)
/// <summary>
/// Error message
/// </summary>
ErrorMessage = 1 << 10, // Data (string)
/// <summary>
/// NAT introduction was successful
/// </summary>
NatIntroductionSuccess = 1 << 11, // Data (as passed to master server)
/// <summary>
/// A roundtrip was measured and NetConnection.AverageRoundtripTime was updated
/// </summary>
ConnectionLatencyUpdated = 1 << 12, // Seconds as a Single
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics.CodeAnalysis;
namespace Lidgren.Network
{
/// <summary>
/// The type of a NetIncomingMessage
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
public enum NetIncomingMessageType
{
//
// library note: values are power-of-two, but they are not flags - it's a convenience for NetPeerConfiguration.DisabledMessageTypes
//
/// <summary>
/// Error; this value should never appear
/// </summary>
Error = 0,
/// <summary>
/// Status for a connection changed
/// </summary>
StatusChanged = 1 << 0, // Data (string)
/// <summary>
/// Data sent using SendUnconnectedMessage
/// </summary>
UnconnectedData = 1 << 1, // Data Based on data received
/// <summary>
/// Connection approval is needed
/// </summary>
ConnectionApproval = 1 << 2, // Data
/// <summary>
/// Application data
/// </summary>
Data = 1 << 3, // Data Based on data received
/// <summary>
/// Receipt of delivery
/// </summary>
Receipt = 1 << 4, // Data
/// <summary>
/// Discovery request for a response
/// </summary>
DiscoveryRequest = 1 << 5, // (no data)
/// <summary>
/// Discovery response to a request
/// </summary>
DiscoveryResponse = 1 << 6, // Data
/// <summary>
/// Verbose debug message
/// </summary>
VerboseDebugMessage = 1 << 7, // Data (string)
/// <summary>
/// Debug message
/// </summary>
DebugMessage = 1 << 8, // Data (string)
/// <summary>
/// Warning message
/// </summary>
WarningMessage = 1 << 9, // Data (string)
/// <summary>
/// Error message
/// </summary>
ErrorMessage = 1 << 10, // Data (string)
/// <summary>
/// NAT introduction was successful
/// </summary>
NatIntroductionSuccess = 1 << 11, // Data (as passed to master server)
/// <summary>
/// A roundtrip was measured and NetConnection.AverageRoundtripTime was updated
/// </summary>
ConnectionLatencyUpdated = 1 << 12, // Seconds as a Single
}
}

View File

@@ -1,175 +1,175 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace Lidgren.Network
{
internal enum NetMessageType : byte
{
Unconnected = 0,
UserUnreliable = 1,
UserSequenced1 = 2,
UserSequenced2 = 3,
UserSequenced3 = 4,
UserSequenced4 = 5,
UserSequenced5 = 6,
UserSequenced6 = 7,
UserSequenced7 = 8,
UserSequenced8 = 9,
UserSequenced9 = 10,
UserSequenced10 = 11,
UserSequenced11 = 12,
UserSequenced12 = 13,
UserSequenced13 = 14,
UserSequenced14 = 15,
UserSequenced15 = 16,
UserSequenced16 = 17,
UserSequenced17 = 18,
UserSequenced18 = 19,
UserSequenced19 = 20,
UserSequenced20 = 21,
UserSequenced21 = 22,
UserSequenced22 = 23,
UserSequenced23 = 24,
UserSequenced24 = 25,
UserSequenced25 = 26,
UserSequenced26 = 27,
UserSequenced27 = 28,
UserSequenced28 = 29,
UserSequenced29 = 30,
UserSequenced30 = 31,
UserSequenced31 = 32,
UserSequenced32 = 33,
UserReliableUnordered = 34,
UserReliableSequenced1 = 35,
UserReliableSequenced2 = 36,
UserReliableSequenced3 = 37,
UserReliableSequenced4 = 38,
UserReliableSequenced5 = 39,
UserReliableSequenced6 = 40,
UserReliableSequenced7 = 41,
UserReliableSequenced8 = 42,
UserReliableSequenced9 = 43,
UserReliableSequenced10 = 44,
UserReliableSequenced11 = 45,
UserReliableSequenced12 = 46,
UserReliableSequenced13 = 47,
UserReliableSequenced14 = 48,
UserReliableSequenced15 = 49,
UserReliableSequenced16 = 50,
UserReliableSequenced17 = 51,
UserReliableSequenced18 = 52,
UserReliableSequenced19 = 53,
UserReliableSequenced20 = 54,
UserReliableSequenced21 = 55,
UserReliableSequenced22 = 56,
UserReliableSequenced23 = 57,
UserReliableSequenced24 = 58,
UserReliableSequenced25 = 59,
UserReliableSequenced26 = 60,
UserReliableSequenced27 = 61,
UserReliableSequenced28 = 62,
UserReliableSequenced29 = 63,
UserReliableSequenced30 = 64,
UserReliableSequenced31 = 65,
UserReliableSequenced32 = 66,
UserReliableOrdered1 = 67,
UserReliableOrdered2 = 68,
UserReliableOrdered3 = 69,
UserReliableOrdered4 = 70,
UserReliableOrdered5 = 71,
UserReliableOrdered6 = 72,
UserReliableOrdered7 = 73,
UserReliableOrdered8 = 74,
UserReliableOrdered9 = 75,
UserReliableOrdered10 = 76,
UserReliableOrdered11 = 77,
UserReliableOrdered12 = 78,
UserReliableOrdered13 = 79,
UserReliableOrdered14 = 80,
UserReliableOrdered15 = 81,
UserReliableOrdered16 = 82,
UserReliableOrdered17 = 83,
UserReliableOrdered18 = 84,
UserReliableOrdered19 = 85,
UserReliableOrdered20 = 86,
UserReliableOrdered21 = 87,
UserReliableOrdered22 = 88,
UserReliableOrdered23 = 89,
UserReliableOrdered24 = 90,
UserReliableOrdered25 = 91,
UserReliableOrdered26 = 92,
UserReliableOrdered27 = 93,
UserReliableOrdered28 = 94,
UserReliableOrdered29 = 95,
UserReliableOrdered30 = 96,
UserReliableOrdered31 = 97,
UserReliableOrdered32 = 98,
Unused1 = 99,
Unused2 = 100,
Unused3 = 101,
Unused4 = 102,
Unused5 = 103,
Unused6 = 104,
Unused7 = 105,
Unused8 = 106,
Unused9 = 107,
Unused10 = 108,
Unused11 = 109,
Unused12 = 110,
Unused13 = 111,
Unused14 = 112,
Unused15 = 113,
Unused16 = 114,
Unused17 = 115,
Unused18 = 116,
Unused19 = 117,
Unused20 = 118,
Unused21 = 119,
Unused22 = 120,
Unused23 = 121,
Unused24 = 122,
Unused25 = 123,
Unused26 = 124,
Unused27 = 125,
Unused28 = 126,
Unused29 = 127,
LibraryError = 128,
Ping = 129, // used for RTT calculation
Pong = 130, // used for RTT calculation
Connect = 131,
ConnectResponse = 132,
ConnectionEstablished = 133,
Acknowledge = 134,
Disconnect = 135,
Discovery = 136,
DiscoveryResponse = 137,
NatPunchMessage = 138, // send between peers
NatIntroduction = 139, // send to master server
ExpandMTURequest = 140,
ExpandMTUSuccess = 141,
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace Lidgren.Network
{
internal enum NetMessageType : byte
{
Unconnected = 0,
UserUnreliable = 1,
UserSequenced1 = 2,
UserSequenced2 = 3,
UserSequenced3 = 4,
UserSequenced4 = 5,
UserSequenced5 = 6,
UserSequenced6 = 7,
UserSequenced7 = 8,
UserSequenced8 = 9,
UserSequenced9 = 10,
UserSequenced10 = 11,
UserSequenced11 = 12,
UserSequenced12 = 13,
UserSequenced13 = 14,
UserSequenced14 = 15,
UserSequenced15 = 16,
UserSequenced16 = 17,
UserSequenced17 = 18,
UserSequenced18 = 19,
UserSequenced19 = 20,
UserSequenced20 = 21,
UserSequenced21 = 22,
UserSequenced22 = 23,
UserSequenced23 = 24,
UserSequenced24 = 25,
UserSequenced25 = 26,
UserSequenced26 = 27,
UserSequenced27 = 28,
UserSequenced28 = 29,
UserSequenced29 = 30,
UserSequenced30 = 31,
UserSequenced31 = 32,
UserSequenced32 = 33,
UserReliableUnordered = 34,
UserReliableSequenced1 = 35,
UserReliableSequenced2 = 36,
UserReliableSequenced3 = 37,
UserReliableSequenced4 = 38,
UserReliableSequenced5 = 39,
UserReliableSequenced6 = 40,
UserReliableSequenced7 = 41,
UserReliableSequenced8 = 42,
UserReliableSequenced9 = 43,
UserReliableSequenced10 = 44,
UserReliableSequenced11 = 45,
UserReliableSequenced12 = 46,
UserReliableSequenced13 = 47,
UserReliableSequenced14 = 48,
UserReliableSequenced15 = 49,
UserReliableSequenced16 = 50,
UserReliableSequenced17 = 51,
UserReliableSequenced18 = 52,
UserReliableSequenced19 = 53,
UserReliableSequenced20 = 54,
UserReliableSequenced21 = 55,
UserReliableSequenced22 = 56,
UserReliableSequenced23 = 57,
UserReliableSequenced24 = 58,
UserReliableSequenced25 = 59,
UserReliableSequenced26 = 60,
UserReliableSequenced27 = 61,
UserReliableSequenced28 = 62,
UserReliableSequenced29 = 63,
UserReliableSequenced30 = 64,
UserReliableSequenced31 = 65,
UserReliableSequenced32 = 66,
UserReliableOrdered1 = 67,
UserReliableOrdered2 = 68,
UserReliableOrdered3 = 69,
UserReliableOrdered4 = 70,
UserReliableOrdered5 = 71,
UserReliableOrdered6 = 72,
UserReliableOrdered7 = 73,
UserReliableOrdered8 = 74,
UserReliableOrdered9 = 75,
UserReliableOrdered10 = 76,
UserReliableOrdered11 = 77,
UserReliableOrdered12 = 78,
UserReliableOrdered13 = 79,
UserReliableOrdered14 = 80,
UserReliableOrdered15 = 81,
UserReliableOrdered16 = 82,
UserReliableOrdered17 = 83,
UserReliableOrdered18 = 84,
UserReliableOrdered19 = 85,
UserReliableOrdered20 = 86,
UserReliableOrdered21 = 87,
UserReliableOrdered22 = 88,
UserReliableOrdered23 = 89,
UserReliableOrdered24 = 90,
UserReliableOrdered25 = 91,
UserReliableOrdered26 = 92,
UserReliableOrdered27 = 93,
UserReliableOrdered28 = 94,
UserReliableOrdered29 = 95,
UserReliableOrdered30 = 96,
UserReliableOrdered31 = 97,
UserReliableOrdered32 = 98,
Unused1 = 99,
Unused2 = 100,
Unused3 = 101,
Unused4 = 102,
Unused5 = 103,
Unused6 = 104,
Unused7 = 105,
Unused8 = 106,
Unused9 = 107,
Unused10 = 108,
Unused11 = 109,
Unused12 = 110,
Unused13 = 111,
Unused14 = 112,
Unused15 = 113,
Unused16 = 114,
Unused17 = 115,
Unused18 = 116,
Unused19 = 117,
Unused20 = 118,
Unused21 = 119,
Unused22 = 120,
Unused23 = 121,
Unused24 = 122,
Unused25 = 123,
Unused26 = 124,
Unused27 = 125,
Unused28 = 126,
Unused29 = 127,
LibraryError = 128,
Ping = 129, // used for RTT calculation
Pong = 130, // used for RTT calculation
Connect = 131,
ConnectResponse = 132,
ConnectionEstablished = 133,
Acknowledge = 134,
Disconnect = 135,
Discovery = 136,
DiscoveryResponse = 137,
NatPunchMessage = 138, // send between peers
NatIntroduction = 139, // send to master server
ExpandMTURequest = 140,
ExpandMTUSuccess = 141,
}
}

View File

@@ -1,110 +1,110 @@
using System;
using System.Collections.Generic;
using System.Net;
namespace Lidgren.Network
{
public partial class NetPeer
{
/// <summary>
/// Send NetIntroduction to hostExternal and clientExternal; introducing client to host
/// </summary>
public void Introduce(
IPEndPoint hostInternal,
IPEndPoint hostExternal,
IPEndPoint clientInternal,
IPEndPoint clientExternal,
string token)
{
// send message to client
NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1);
msg.m_messageType = NetMessageType.NatIntroduction;
msg.Write((byte)0);
msg.Write(hostInternal);
msg.Write(hostExternal);
msg.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(clientExternal, msg));
// send message to host
msg = CreateMessage(10 + token.Length + 1);
msg.m_messageType = NetMessageType.NatIntroduction;
msg.Write((byte)1);
msg.Write(clientInternal);
msg.Write(clientExternal);
msg.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(hostExternal, msg));
}
/// <summary>
/// Called when host/client receives a NatIntroduction message from a master server
/// </summary>
private void HandleNatIntroduction(int ptr)
{
VerifyNetworkThread();
// read intro
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
byte hostByte = tmp.ReadByte();
IPEndPoint remoteInternal = tmp.ReadIPEndPoint();
IPEndPoint remoteExternal = tmp.ReadIPEndPoint();
string token = tmp.ReadString();
bool isHost = (hostByte != 0);
LogDebug("NAT introduction received; we are designated " + (isHost ? "host" : "client"));
NetOutgoingMessage punch;
if (!isHost && m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess) == false)
return; // no need to punch - we're not listening for nat intros!
// send internal punch
punch = CreateMessage(1);
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write(hostByte);
punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteInternal, punch));
// send external punch
punch = CreateMessage(1);
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write(hostByte);
punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteExternal, punch));
}
/// <summary>
/// Called when receiving a NatPunchMessage from a remote endpoint
/// </summary>
private void HandleNatPunch(int ptr, IPEndPoint senderEndPoint)
{
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
byte fromHostByte = tmp.ReadByte();
if (fromHostByte == 0)
{
// it's from client
LogDebug("NAT punch received from " + senderEndPoint + " we're host, so we ignore this");
return; // don't alert hosts about nat punch successes; only clients
}
string token = tmp.ReadString();
LogDebug("NAT punch received from " + senderEndPoint + " we're client, so we've succeeded - token is " + token);
//
// Release punch success to client; enabling him to Connect() to msg.SenderIPEndPoint if token is ok
//
NetIncomingMessage punchSuccess = CreateIncomingMessage(NetIncomingMessageType.NatIntroductionSuccess, 10);
punchSuccess.m_senderEndPoint = senderEndPoint;
punchSuccess.Write(token);
ReleaseMessage(punchSuccess);
// send a return punch just for good measure
var punch = CreateMessage(1);
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write((byte)0);
punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(senderEndPoint, punch));
}
}
}
using System;
using System.Collections.Generic;
using System.Net;
namespace Lidgren.Network
{
public partial class NetPeer
{
/// <summary>
/// Send NetIntroduction to hostExternal and clientExternal; introducing client to host
/// </summary>
public void Introduce(
IPEndPoint hostInternal,
IPEndPoint hostExternal,
IPEndPoint clientInternal,
IPEndPoint clientExternal,
string token)
{
// send message to client
NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1);
msg.m_messageType = NetMessageType.NatIntroduction;
msg.Write((byte)0);
msg.Write(hostInternal);
msg.Write(hostExternal);
msg.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(clientExternal, msg));
// send message to host
msg = CreateMessage(10 + token.Length + 1);
msg.m_messageType = NetMessageType.NatIntroduction;
msg.Write((byte)1);
msg.Write(clientInternal);
msg.Write(clientExternal);
msg.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(hostExternal, msg));
}
/// <summary>
/// Called when host/client receives a NatIntroduction message from a master server
/// </summary>
private void HandleNatIntroduction(int ptr)
{
VerifyNetworkThread();
// read intro
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
byte hostByte = tmp.ReadByte();
IPEndPoint remoteInternal = tmp.ReadIPEndPoint();
IPEndPoint remoteExternal = tmp.ReadIPEndPoint();
string token = tmp.ReadString();
bool isHost = (hostByte != 0);
LogDebug("NAT introduction received; we are designated " + (isHost ? "host" : "client"));
NetOutgoingMessage punch;
if (!isHost && m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess) == false)
return; // no need to punch - we're not listening for nat intros!
// send internal punch
punch = CreateMessage(1);
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write(hostByte);
punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteInternal, punch));
// send external punch
punch = CreateMessage(1);
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write(hostByte);
punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteExternal, punch));
}
/// <summary>
/// Called when receiving a NatPunchMessage from a remote endpoint
/// </summary>
private void HandleNatPunch(int ptr, IPEndPoint senderEndPoint)
{
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
byte fromHostByte = tmp.ReadByte();
if (fromHostByte == 0)
{
// it's from client
LogDebug("NAT punch received from " + senderEndPoint + " we're host, so we ignore this");
return; // don't alert hosts about nat punch successes; only clients
}
string token = tmp.ReadString();
LogDebug("NAT punch received from " + senderEndPoint + " we're client, so we've succeeded - token is " + token);
//
// Release punch success to client; enabling him to Connect() to msg.SenderIPEndPoint if token is ok
//
NetIncomingMessage punchSuccess = CreateIncomingMessage(NetIncomingMessageType.NatIntroductionSuccess, 10);
punchSuccess.m_senderEndPoint = senderEndPoint;
punchSuccess.Write(token);
ReleaseMessage(punchSuccess);
// send a return punch just for good measure
var punch = CreateMessage(1);
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write((byte)0);
punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(senderEndPoint, punch));
}
}
}

View File

@@ -1,91 +1,91 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetOutgoingMessage
{
/// <summary>
/// Writes all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void WriteAllFields(object ob)
{
WriteAllFields(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Writes all fields with specified binding in alphabetical order using reflection
/// </summary>
public void WriteAllFields(object ob, BindingFlags flags)
{
if (ob == null)
return;
Type tp = ob.GetType();
FieldInfo[] fields = tp.GetFields(flags);
NetUtility.SortMembersList(fields);
foreach (FieldInfo fi in fields)
{
object value = fi.GetValue(ob);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.FieldType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
else
throw new NetException("Failed to find write method for type " + fi.FieldType);
}
}
/// <summary>
/// Writes all public and private declared instance properties of the object in alphabetical order using reflection
/// </summary>
public void WriteAllProperties(object ob)
{
WriteAllProperties(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Writes all properties with specified binding in alphabetical order using reflection
/// </summary>
public void WriteAllProperties(object ob, BindingFlags flags)
{
if (ob == null)
return;
Type tp = ob.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
NetUtility.SortMembersList(fields);
foreach (PropertyInfo fi in fields)
{
MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
object value = getMethod.Invoke(ob, null);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Reflection;
namespace Lidgren.Network
{
public partial class NetOutgoingMessage
{
/// <summary>
/// Writes all public and private declared instance fields of the object in alphabetical order using reflection
/// </summary>
public void WriteAllFields(object ob)
{
WriteAllFields(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Writes all fields with specified binding in alphabetical order using reflection
/// </summary>
public void WriteAllFields(object ob, BindingFlags flags)
{
if (ob == null)
return;
Type tp = ob.GetType();
FieldInfo[] fields = tp.GetFields(flags);
NetUtility.SortMembersList(fields);
foreach (FieldInfo fi in fields)
{
object value = fi.GetValue(ob);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.FieldType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
else
throw new NetException("Failed to find write method for type " + fi.FieldType);
}
}
/// <summary>
/// Writes all public and private declared instance properties of the object in alphabetical order using reflection
/// </summary>
public void WriteAllProperties(object ob)
{
WriteAllProperties(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
/// <summary>
/// Writes all properties with specified binding in alphabetical order using reflection
/// </summary>
public void WriteAllProperties(object ob, BindingFlags flags)
{
if (ob == null)
return;
Type tp = ob.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
NetUtility.SortMembersList(fields);
foreach (PropertyInfo fi in fields)
{
MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
object value = getMethod.Invoke(ob, null);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,132 +1,132 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
namespace Lidgren.Network
{
/// <summary>
/// Outgoing message used to send data to remote peer(s)
/// </summary>
[DebuggerDisplay("LengthBits={LengthBits}")]
public sealed class NetOutgoingMessage : NetBuffer
{
internal NetMessageType m_messageType;
internal bool m_isSent;
internal int m_recyclingCount;
internal int m_fragmentGroup; // which group of fragments ths belongs to
internal int m_fragmentGroupTotalBits; // total number of bits in this group
internal int m_fragmentChunkByteSize; // size, in bytes, of every chunk but the last one
internal int m_fragmentChunkNumber; // which number chunk this is, starting with 0
internal NetOutgoingMessage()
{
}
internal void Reset()
{
m_messageType = NetMessageType.LibraryError;
m_bitLength = 0;
m_isSent = false;
m_recyclingCount = 0;
m_fragmentGroup = 0;
}
internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
{
// 8 bits - NetMessageType
// 1 bit - Fragment?
// 15 bits - Sequence number
// 16 bits - Payload length in bits
intoBuffer[ptr++] = (byte)m_messageType;
byte low = (byte)((sequenceNumber << 1) | (m_fragmentGroup == 0 ? 0 : 1));
intoBuffer[ptr++] = low;
intoBuffer[ptr++] = (byte)(sequenceNumber >> 7);
if (m_fragmentGroup == 0)
{
intoBuffer[ptr++] = (byte)m_bitLength;
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
if (byteLen > 0)
{
Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen);
ptr += byteLen;
}
}
else
{
int wasPtr = ptr;
intoBuffer[ptr++] = (byte)m_bitLength;
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
//
// write fragmentation header
//
ptr = NetFragmentationHelper.WriteHeader(intoBuffer, ptr, m_fragmentGroup, m_fragmentGroupTotalBits, m_fragmentChunkByteSize, m_fragmentChunkNumber);
int hdrLen = ptr - wasPtr - 2;
// update length
int realBitLength = m_bitLength + (hdrLen * 8);
intoBuffer[wasPtr] = (byte)realBitLength;
intoBuffer[wasPtr + 1] = (byte)(realBitLength >> 8);
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
if (byteLen > 0)
{
Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
ptr += byteLen;
}
}
NetException.Assert(ptr > 0);
return ptr;
}
internal int GetEncodedSize()
{
int retval = NetConstants.UnfragmentedMessageHeaderSize; // regular headers
if (m_fragmentGroup != 0)
retval += NetFragmentationHelper.GetFragmentationHeaderSize(m_fragmentGroup, m_fragmentGroupTotalBits / 8, m_fragmentChunkByteSize, m_fragmentChunkNumber);
retval += this.LengthBytes;
return retval;
}
/// <summary>
/// Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt!
/// </summary>
public bool Encrypt(INetEncryption encryption)
{
return encryption.Encrypt(this);
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
return "[NetOutgoingMessage " + m_messageType + " " + this.LengthBytes + " bytes]";
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
namespace Lidgren.Network
{
/// <summary>
/// Outgoing message used to send data to remote peer(s)
/// </summary>
[DebuggerDisplay("LengthBits={LengthBits}")]
public sealed class NetOutgoingMessage : NetBuffer
{
internal NetMessageType m_messageType;
internal bool m_isSent;
internal int m_recyclingCount;
internal int m_fragmentGroup; // which group of fragments ths belongs to
internal int m_fragmentGroupTotalBits; // total number of bits in this group
internal int m_fragmentChunkByteSize; // size, in bytes, of every chunk but the last one
internal int m_fragmentChunkNumber; // which number chunk this is, starting with 0
internal NetOutgoingMessage()
{
}
internal void Reset()
{
m_messageType = NetMessageType.LibraryError;
m_bitLength = 0;
m_isSent = false;
m_recyclingCount = 0;
m_fragmentGroup = 0;
}
internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
{
// 8 bits - NetMessageType
// 1 bit - Fragment?
// 15 bits - Sequence number
// 16 bits - Payload length in bits
intoBuffer[ptr++] = (byte)m_messageType;
byte low = (byte)((sequenceNumber << 1) | (m_fragmentGroup == 0 ? 0 : 1));
intoBuffer[ptr++] = low;
intoBuffer[ptr++] = (byte)(sequenceNumber >> 7);
if (m_fragmentGroup == 0)
{
intoBuffer[ptr++] = (byte)m_bitLength;
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
if (byteLen > 0)
{
Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen);
ptr += byteLen;
}
}
else
{
int wasPtr = ptr;
intoBuffer[ptr++] = (byte)m_bitLength;
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
//
// write fragmentation header
//
ptr = NetFragmentationHelper.WriteHeader(intoBuffer, ptr, m_fragmentGroup, m_fragmentGroupTotalBits, m_fragmentChunkByteSize, m_fragmentChunkNumber);
int hdrLen = ptr - wasPtr - 2;
// update length
int realBitLength = m_bitLength + (hdrLen * 8);
intoBuffer[wasPtr] = (byte)realBitLength;
intoBuffer[wasPtr + 1] = (byte)(realBitLength >> 8);
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
if (byteLen > 0)
{
Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
ptr += byteLen;
}
}
NetException.Assert(ptr > 0);
return ptr;
}
internal int GetEncodedSize()
{
int retval = NetConstants.UnfragmentedMessageHeaderSize; // regular headers
if (m_fragmentGroup != 0)
retval += NetFragmentationHelper.GetFragmentationHeaderSize(m_fragmentGroup, m_fragmentGroupTotalBits / 8, m_fragmentChunkByteSize, m_fragmentChunkNumber);
retval += this.LengthBytes;
return retval;
}
/// <summary>
/// Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt!
/// </summary>
public bool Encrypt(INetEncryption encryption)
{
return encryption.Encrypt(this);
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
return "[NetOutgoingMessage " + m_messageType + " " + this.LengthBytes + " bytes]";
}
}
}

View File

@@ -1,60 +1,60 @@
using System;
using System.Net;
namespace Lidgren.Network
{
public partial class NetPeer
{
/// <summary>
/// Emit a discovery signal to all hosts on your subnet
/// </summary>
public void DiscoverLocalPeers(int serverPort)
{
NetOutgoingMessage om = CreateMessage(0);
om.m_messageType = NetMessageType.Discovery;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(IPAddress.Broadcast, serverPort), om));
}
/// <summary>
/// Emit a discovery signal to a single known host
/// </summary>
public bool DiscoverKnownPeer(string host, int serverPort)
{
IPAddress address = NetUtility.Resolve(host);
if (address == null)
return false;
DiscoverKnownPeer(new IPEndPoint(address, serverPort));
return true;
}
/// <summary>
/// Emit a discovery signal to a single known host
/// </summary>
public void DiscoverKnownPeer(IPEndPoint endPoint)
{
NetOutgoingMessage om = CreateMessage(0);
om.m_messageType = NetMessageType.Discovery;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(endPoint, om));
}
/// <summary>
/// Send a discovery response message
/// </summary>
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
{
if (recipient == null)
throw new ArgumentNullException("recipient");
if (msg == null)
msg = CreateMessage(0);
else if (msg.m_isSent)
throw new NetException("Message has already been sent!");
if (msg.LengthBytes >= m_configuration.MaximumTransmissionUnit)
throw new NetException("Cannot send discovery message larger than MTU (currently " + m_configuration.MaximumTransmissionUnit + " bytes)");
msg.m_messageType = NetMessageType.DiscoveryResponse;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
}
}
}
using System;
using System.Net;
namespace Lidgren.Network
{
public partial class NetPeer
{
/// <summary>
/// Emit a discovery signal to all hosts on your subnet
/// </summary>
public void DiscoverLocalPeers(int serverPort)
{
NetOutgoingMessage om = CreateMessage(0);
om.m_messageType = NetMessageType.Discovery;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(IPAddress.Broadcast, serverPort), om));
}
/// <summary>
/// Emit a discovery signal to a single known host
/// </summary>
public bool DiscoverKnownPeer(string host, int serverPort)
{
IPAddress address = NetUtility.Resolve(host);
if (address == null)
return false;
DiscoverKnownPeer(new IPEndPoint(address, serverPort));
return true;
}
/// <summary>
/// Emit a discovery signal to a single known host
/// </summary>
public void DiscoverKnownPeer(IPEndPoint endPoint)
{
NetOutgoingMessage om = CreateMessage(0);
om.m_messageType = NetMessageType.Discovery;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(endPoint, om));
}
/// <summary>
/// Send a discovery response message
/// </summary>
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
{
if (recipient == null)
throw new ArgumentNullException("recipient");
if (msg == null)
msg = CreateMessage(0);
else if (msg.m_isSent)
throw new NetException("Message has already been sent!");
if (msg.LengthBytes >= m_configuration.MaximumTransmissionUnit)
throw new NetException("Cannot send discovery message larger than MTU (currently " + m_configuration.MaximumTransmissionUnit + " bytes)");
msg.m_messageType = NetMessageType.DiscoveryResponse;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
}
}
}

View File

@@ -1,159 +1,158 @@
using System;
using System.Threading;
using System.Collections.Generic;
namespace Lidgren.Network
{
internal class ReceivedFragmentGroup
{
public float LastReceived;
public byte[] Data;
public NetBitVector ReceivedChunks;
}
public partial class NetPeer
{
private int m_lastUsedFragmentGroup;
private Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>> m_receivedFragmentGroups;
// on user thread
private void SendFragmentedMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
{
// Note: this group id is PER SENDING/NetPeer; ie. same id is sent to all recipients;
// this should be ok however; as long as recipients differentiate between same id but different sender
int group = Interlocked.Increment(ref m_lastUsedFragmentGroup);
if (group >= NetConstants.MaxFragmentationGroups)
{
// @TODO: not thread safe; but in practice probably not an issue
m_lastUsedFragmentGroup = 1;
group = 1;
}
msg.m_fragmentGroup = group;
// do not send msg; but set fragmentgroup in case user tries to recycle it immediately
// create fragmentation specifics
int totalBytes = msg.LengthBytes;
// determine minimum mtu for all recipients
int mtu = GetMTU(recipients);
int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu);
int numChunks = totalBytes / bytesPerChunk;
if (numChunks * bytesPerChunk < totalBytes)
numChunks++;
int bitsPerChunk = bytesPerChunk * 8;
int bitsLeft = msg.LengthBits;
for (int i = 0; i < numChunks; i++)
{
NetOutgoingMessage chunk = CreateMessage(mtu);
chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft);
chunk.m_data = msg.m_data;
chunk.m_fragmentGroup = group;
chunk.m_fragmentGroupTotalBits = totalBytes * 8;
chunk.m_fragmentChunkByteSize = bytesPerChunk;
chunk.m_fragmentChunkNumber = i;
NetException.Assert(chunk.m_bitLength != 0);
NetException.Assert(chunk.GetEncodedSize() < mtu);
Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count);
foreach (NetConnection recipient in recipients)
recipient.EnqueueMessage(chunk, method, sequenceChannel);
bitsLeft -= bitsPerChunk;
}
return;
}
private void HandleReleasedFragment(NetIncomingMessage im)
{
//
// read fragmentation header and combine fragments
//
int group;
int totalBits;
int chunkByteSize;
int chunkNumber;
int ptr = NetFragmentationHelper.ReadHeader(
im.m_data, 0,
out group,
out totalBits,
out chunkByteSize,
out chunkNumber
);
NetException.Assert(im.LengthBytes > ptr);
NetException.Assert(group > 0);
NetException.Assert(totalBits > 0);
NetException.Assert(chunkByteSize > 0);
int totalBytes = NetUtility.BytesToHoldBits((int)totalBits);
int totalNumChunks = totalBytes / chunkByteSize;
if (totalNumChunks * chunkByteSize < totalBytes)
totalNumChunks++;
NetException.Assert(chunkNumber < totalNumChunks);
if (chunkNumber >= totalNumChunks)
{
LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
return;
}
Dictionary<int, ReceivedFragmentGroup> groups;
if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups))
{
groups = new Dictionary<int, ReceivedFragmentGroup>();
m_receivedFragmentGroups[im.SenderConnection] = groups;
}
ReceivedFragmentGroup info;
if (!groups.TryGetValue(group, out info))
{
info = new ReceivedFragmentGroup();
info.Data = new byte[totalBytes];
info.ReceivedChunks = new NetBitVector(totalNumChunks);
groups[group] = info;
}
info.ReceivedChunks[chunkNumber] = true;
info.LastReceived = (float)NetTime.Now;
// copy to data
int offset = (chunkNumber * chunkByteSize);
Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr);
int cnt = info.ReceivedChunks.Count();
//LogVerbose("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")");
LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");
if (info.ReceivedChunks.Count() == totalNumChunks)
{
// Done! Transform this incoming message
im.m_data = info.Data;
im.m_bitLength = (int)totalBits;
im.m_isFragment = false;
LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");
groups.Remove(group);
ReleaseMessage(im);
}
else
{
// data has been copied; recycle this incoming message
Recycle(im);
}
return;
}
}
}
using System;
using System.Threading;
using System.Collections.Generic;
namespace Lidgren.Network
{
internal class ReceivedFragmentGroup
{
public float LastReceived;
public byte[] Data;
public NetBitVector ReceivedChunks;
}
public partial class NetPeer
{
private int m_lastUsedFragmentGroup;
private Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>> m_receivedFragmentGroups;
// on user thread
private void SendFragmentedMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
{
// Note: this group id is PER SENDING/NetPeer; ie. same id is sent to all recipients;
// this should be ok however; as long as recipients differentiate between same id but different sender
int group = Interlocked.Increment(ref m_lastUsedFragmentGroup);
if (group >= NetConstants.MaxFragmentationGroups)
{
// @TODO: not thread safe; but in practice probably not an issue
m_lastUsedFragmentGroup = 1;
group = 1;
}
msg.m_fragmentGroup = group;
// do not send msg; but set fragmentgroup in case user tries to recycle it immediately
// create fragmentation specifics
int totalBytes = msg.LengthBytes;
// determine minimum mtu for all recipients
int mtu = GetMTU(recipients);
int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu);
int numChunks = totalBytes / bytesPerChunk;
if (numChunks * bytesPerChunk < totalBytes)
numChunks++;
int bitsPerChunk = bytesPerChunk * 8;
int bitsLeft = msg.LengthBits;
for (int i = 0; i < numChunks; i++)
{
NetOutgoingMessage chunk = CreateMessage(mtu);
chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft);
chunk.m_data = msg.m_data;
chunk.m_fragmentGroup = group;
chunk.m_fragmentGroupTotalBits = totalBytes * 8;
chunk.m_fragmentChunkByteSize = bytesPerChunk;
chunk.m_fragmentChunkNumber = i;
NetException.Assert(chunk.m_bitLength != 0);
NetException.Assert(chunk.GetEncodedSize() < mtu);
Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count);
foreach (NetConnection recipient in recipients)
recipient.EnqueueMessage(chunk, method, sequenceChannel);
bitsLeft -= bitsPerChunk;
}
return;
}
private void HandleReleasedFragment(NetIncomingMessage im)
{
//
// read fragmentation header and combine fragments
//
int group;
int totalBits;
int chunkByteSize;
int chunkNumber;
int ptr = NetFragmentationHelper.ReadHeader(
im.m_data, 0,
out group,
out totalBits,
out chunkByteSize,
out chunkNumber
);
NetException.Assert(im.LengthBytes > ptr);
NetException.Assert(group > 0);
NetException.Assert(totalBits > 0);
NetException.Assert(chunkByteSize > 0);
int totalBytes = NetUtility.BytesToHoldBits((int)totalBits);
int totalNumChunks = totalBytes / chunkByteSize;
if (totalNumChunks * chunkByteSize < totalBytes)
totalNumChunks++;
NetException.Assert(chunkNumber < totalNumChunks);
if (chunkNumber >= totalNumChunks)
{
LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
return;
}
Dictionary<int, ReceivedFragmentGroup> groups;
if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups))
{
groups = new Dictionary<int, ReceivedFragmentGroup>();
m_receivedFragmentGroups[im.SenderConnection] = groups;
}
ReceivedFragmentGroup info;
if (!groups.TryGetValue(group, out info))
{
info = new ReceivedFragmentGroup();
info.Data = new byte[totalBytes];
info.ReceivedChunks = new NetBitVector(totalNumChunks);
groups[group] = info;
}
info.ReceivedChunks[chunkNumber] = true;
info.LastReceived = (float)NetTime.Now;
// copy to data
int offset = (chunkNumber * chunkByteSize);
Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr);
int cnt = info.ReceivedChunks.Count();
LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");
if (info.ReceivedChunks.Count() == totalNumChunks)
{
// Done! Transform this incoming message
im.m_data = info.Data;
im.m_bitLength = (int)totalBits;
im.m_isFragment = false;
LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");
groups.Remove(group);
ReleaseMessage(im);
}
else
{
// data has been copied; recycle this incoming message
Recycle(im);
}
return;
}
}
}

View File

@@ -370,9 +370,6 @@ namespace Lidgren.Network
if (!m_socket.Poll(1000, SelectMode.SelectRead)) // wait up to 1 ms for data to arrive
return;
//if (m_socket == null || m_socket.Available < 1)
// return;
do
{
int bytesReceived = 0;
@@ -398,8 +395,6 @@ namespace Lidgren.Network
if (bytesReceived < NetConstants.HeaderByteSize)
return;
//LogVerbose("Received " + bytesReceived + " bytes");
IPEndPoint ipsender = (IPEndPoint)m_senderRemote;
if (ipsender.Port == 1900)
@@ -648,7 +643,6 @@ namespace Lidgren.Network
internal void AcceptConnection(NetConnection conn)
{
// LogDebug("Accepted connection " + conn);
conn.InitExpandMTU(NetTime.Now);
if (m_handshakes.Remove(conn.m_remoteEndPoint) == false)

View File

@@ -1,304 +1,299 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//#define USE_RELEASE_STATISTICS
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
namespace Lidgren.Network
{
public partial class NetPeer
{
#if DEBUG
private readonly List<DelayedPacket> m_delayedPackets = new List<DelayedPacket>();
private class DelayedPacket
{
public byte[] Data;
public double DelayedUntil;
public IPEndPoint Target;
}
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset)
{
connectionReset = false;
// simulate loss
float loss = m_configuration.m_loss;
if (loss > 0.0f)
{
if ((float)NetRandom.Instance.NextDouble() < loss)
{
LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!");
return; // packet "lost"
}
}
m_statistics.PacketSent(numBytes, numMessages);
// simulate latency
float m = m_configuration.m_minimumOneWayLatency;
float r = m_configuration.m_randomOneWayLatency;
if (m == 0.0f && r == 0.0f)
{
// no latency simulation
// LogVerbose("Sending packet " + numBytes + " bytes");
bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
// TODO: handle wasSent == false?
return;
}
int num = 1;
if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.NextSingle() < m_configuration.m_duplicates)
num++;
float delay = 0;
for (int i = 0; i < num; i++)
{
delay = m_configuration.m_minimumOneWayLatency + (NetRandom.Instance.NextSingle() * m_configuration.m_randomOneWayLatency);
// Enqueue delayed packet
DelayedPacket p = new DelayedPacket();
p.Target = target;
p.Data = new byte[numBytes];
Buffer.BlockCopy(m_sendBuffer, 0, p.Data, 0, numBytes);
p.DelayedUntil = NetTime.Now + delay;
m_delayedPackets.Add(p);
}
// LogVerbose("Sending packet " + numBytes + " bytes - delayed " + NetTime.ToReadable(delay));
}
private void SendDelayedPackets()
{
if (m_delayedPackets.Count <= 0)
return;
double now = NetTime.Now;
bool connectionReset;
RestartDelaySending:
foreach (DelayedPacket p in m_delayedPackets)
{
if (now > p.DelayedUntil)
{
ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset);
m_delayedPackets.Remove(p);
goto RestartDelaySending;
}
}
}
private void FlushDelayedPackets()
{
try
{
bool connectionReset;
foreach (DelayedPacket p in m_delayedPackets)
ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset);
m_delayedPackets.Clear();
}
catch { }
}
internal bool ActuallySendPacket(byte[] data, int numBytes, IPEndPoint target, out bool connectionReset)
{
connectionReset = false;
try
{
// TODO: refactor this check outta here
if (target.Address == IPAddress.Broadcast)
{
// Some networks do not allow
// a global broadcast so we use the BroadcastAddress from the configuration
// this can be resolved to a local broadcast addresss e.g 192.168.x.255
target.Address = m_configuration.BroadcastAddress;
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
}
int bytesSent = m_socket.SendTo(data, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
// LogDebug("Sent " + numBytes + " bytes");
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return false;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
{
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
connectionReset = true;
return false;
}
LogError("Failed to send packet: " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
if (target.Address == IPAddress.Broadcast)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
}
return true;
}
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
{
try
{
m_socket.DontFragment = true;
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
m_statistics.PacketSent(numBytes, 1);
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.MessageSize)
return false;
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return true;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
return true;
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
m_socket.DontFragment = false;
}
return true;
}
#else
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
{
try
{
m_socket.DontFragment = true;
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.MessageSize)
return false;
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return true;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
return true;
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
m_socket.DontFragment = false;
}
return true;
}
//
// Release - just send the packet straight away
//
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset)
{
#if USE_RELEASE_STATISTICS
m_statistics.PacketSent(numBytes, numMessages);
#endif
connectionReset = false;
try
{
// TODO: refactor this check outta here
if (target.Address == IPAddress.Broadcast)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
{
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
connectionReset = true;
return;
}
LogError("Failed to send packet: " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
if (target.Address == IPAddress.Broadcast)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
}
return;
}
private void FlushDelayedPackets()
{
}
private void SendCallBack(IAsyncResult res)
{
NetException.Assert(res.IsCompleted == true);
m_socket.EndSendTo(res);
}
#endif
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//#define USE_RELEASE_STATISTICS
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
namespace Lidgren.Network
{
public partial class NetPeer
{
#if DEBUG
private readonly List<DelayedPacket> m_delayedPackets = new List<DelayedPacket>();
private class DelayedPacket
{
public byte[] Data;
public double DelayedUntil;
public IPEndPoint Target;
}
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset)
{
connectionReset = false;
// simulate loss
float loss = m_configuration.m_loss;
if (loss > 0.0f)
{
if ((float)NetRandom.Instance.NextDouble() < loss)
{
LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!");
return; // packet "lost"
}
}
m_statistics.PacketSent(numBytes, numMessages);
// simulate latency
float m = m_configuration.m_minimumOneWayLatency;
float r = m_configuration.m_randomOneWayLatency;
if (m == 0.0f && r == 0.0f)
{
// no latency simulation
bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
// TODO: handle wasSent == false?
return;
}
int num = 1;
if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.NextSingle() < m_configuration.m_duplicates)
num++;
float delay = 0;
for (int i = 0; i < num; i++)
{
delay = m_configuration.m_minimumOneWayLatency + (NetRandom.Instance.NextSingle() * m_configuration.m_randomOneWayLatency);
// Enqueue delayed packet
DelayedPacket p = new DelayedPacket();
p.Target = target;
p.Data = new byte[numBytes];
Buffer.BlockCopy(m_sendBuffer, 0, p.Data, 0, numBytes);
p.DelayedUntil = NetTime.Now + delay;
m_delayedPackets.Add(p);
}
}
private void SendDelayedPackets()
{
if (m_delayedPackets.Count <= 0)
return;
double now = NetTime.Now;
bool connectionReset;
RestartDelaySending:
foreach (DelayedPacket p in m_delayedPackets)
{
if (now > p.DelayedUntil)
{
ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset);
m_delayedPackets.Remove(p);
goto RestartDelaySending;
}
}
}
private void FlushDelayedPackets()
{
try
{
bool connectionReset;
foreach (DelayedPacket p in m_delayedPackets)
ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset);
m_delayedPackets.Clear();
}
catch { }
}
internal bool ActuallySendPacket(byte[] data, int numBytes, IPEndPoint target, out bool connectionReset)
{
connectionReset = false;
try
{
// TODO: refactor this check outta here
if (target.Address == IPAddress.Broadcast)
{
// Some networks do not allow
// a global broadcast so we use the BroadcastAddress from the configuration
// this can be resolved to a local broadcast addresss e.g 192.168.x.255
target.Address = m_configuration.BroadcastAddress;
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
}
int bytesSent = m_socket.SendTo(data, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return false;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
{
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
connectionReset = true;
return false;
}
LogError("Failed to send packet: " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
if (target.Address == IPAddress.Broadcast)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
}
return true;
}
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
{
try
{
m_socket.DontFragment = true;
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
m_statistics.PacketSent(numBytes, 1);
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.MessageSize)
return false;
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return true;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
return true;
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
m_socket.DontFragment = false;
}
return true;
}
#else
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
{
try
{
m_socket.DontFragment = true;
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.MessageSize)
return false;
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return true;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
return true;
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
m_socket.DontFragment = false;
}
return true;
}
//
// Release - just send the packet straight away
//
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset)
{
#if USE_RELEASE_STATISTICS
m_statistics.PacketSent(numBytes, numMessages);
#endif
connectionReset = false;
try
{
// TODO: refactor this check outta here
if (target.Address == IPAddress.Broadcast)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
{
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
connectionReset = true;
return;
}
LogError("Failed to send packet: " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
if (target.Address == IPAddress.Broadcast)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
}
return;
}
private void FlushDelayedPackets()
{
}
private void SendCallBack(IAsyncResult res)
{
NetException.Assert(res.IsCompleted == true);
m_socket.EndSendTo(res);
}
#endif
}
}

View File

@@ -1,51 +1,51 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System.Diagnostics;
namespace Lidgren.Network
{
public partial class NetPeer
{
[Conditional("DEBUG")]
internal void LogVerbose(string message)
{
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.VerboseDebugMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.VerboseDebugMessage, message));
}
[Conditional("DEBUG")]
internal void LogDebug(string message)
{
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DebugMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.DebugMessage, message));
}
internal void LogWarning(string message)
{
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.WarningMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.WarningMessage, message));
}
internal void LogError(string message)
{
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ErrorMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.ErrorMessage, message));
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System.Diagnostics;
namespace Lidgren.Network
{
public partial class NetPeer
{
[Conditional("DEBUG")]
internal void LogVerbose(string message)
{
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.VerboseDebugMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.VerboseDebugMessage, message));
}
[Conditional("DEBUG")]
internal void LogDebug(string message)
{
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DebugMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.DebugMessage, message));
}
internal void LogWarning(string message)
{
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.WarningMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.WarningMessage, message));
}
internal void LogError(string message)
{
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ErrorMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.ErrorMessage, message));
}
}
}

View File

@@ -1,228 +1,228 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
public partial class NetPeer
{
private List<byte[]> m_storagePool; // sorted smallest to largest
private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool;
private NetQueue<NetIncomingMessage> m_incomingMessagesPool;
internal int m_storagePoolBytes;
private void InitializePools()
{
if (m_configuration.UseMessageRecycling)
{
m_storagePool = new List<byte[]>(16);
m_outgoingMessagesPool = new NetQueue<NetOutgoingMessage>(4);
m_incomingMessagesPool = new NetQueue<NetIncomingMessage>(4);
}
else
{
m_storagePool = null;
m_outgoingMessagesPool = null;
m_incomingMessagesPool = null;
}
}
internal byte[] GetStorage(int minimumCapacityInBytes)
{
if (m_storagePool == null)
return new byte[minimumCapacityInBytes];
lock (m_storagePool)
{
for (int i = 0; i < m_storagePool.Count; i++)
{
byte[] retval = m_storagePool[i];
if (retval != null && retval.Length >= minimumCapacityInBytes)
{
m_storagePool[i] = null;
m_storagePoolBytes -= retval.Length;
return retval;
}
}
}
m_statistics.m_bytesAllocated += minimumCapacityInBytes;
return new byte[minimumCapacityInBytes];
}
internal void Recycle(byte[] storage)
{
if (m_storagePool == null)
return;
lock (m_storagePool)
{
m_storagePoolBytes += storage.Length;
int cnt = m_storagePool.Count;
for (int i = 0; i < cnt; i++)
{
if (m_storagePool[i] == null)
{
m_storagePool[i] = storage;
return;
}
}
m_storagePool.Add(storage);
}
}
/// <summary>
/// Creates a new message for sending
/// </summary>
public NetOutgoingMessage CreateMessage()
{
return CreateMessage(m_configuration.m_defaultOutgoingMessageCapacity);
}
/// <summary>
/// Creates a new message for sending and writes the provided string to it
/// </summary>
public NetOutgoingMessage CreateMessage(string content)
{
byte[] bytes = Encoding.UTF8.GetBytes(content);
NetOutgoingMessage om = CreateMessage(2 + bytes.Length);
om.WriteVariableUInt32((uint)bytes.Length);
om.Write(bytes);
return om;
}
/// <summary>
/// Creates a new message for sending
/// </summary>
/// <param name="initialCapacity">initial capacity in bytes</param>
public NetOutgoingMessage CreateMessage(int initialCapacity)
{
NetOutgoingMessage retval;
if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval))
retval = new NetOutgoingMessage();
byte[] storage = GetStorage(initialCapacity);
retval.m_data = storage;
return retval;
}
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] useStorageData)
{
NetIncomingMessage retval;
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
retval = new NetIncomingMessage(tp);
else
retval.m_incomingMessageType = tp;
retval.m_data = useStorageData;
return retval;
}
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, int minimumByteSize)
{
NetIncomingMessage retval;
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
retval = new NetIncomingMessage(tp);
else
retval.m_incomingMessageType = tp;
retval.m_data = GetStorage(minimumByteSize);
return retval;
}
/// <summary>
/// Recycles a NetIncomingMessage instance for reuse; taking pressure off the garbage collector
/// </summary>
public void Recycle(NetIncomingMessage msg)
{
if (m_incomingMessagesPool == null)
return;
#if DEBUG
if (m_incomingMessagesPool.Contains(msg))
throw new NetException("Recyling already recycled message! Thread race?");
#endif
byte[] storage = msg.m_data;
msg.m_data = null;
Recycle(storage);
msg.Reset();
m_incomingMessagesPool.Enqueue(msg);
}
/// <summary>
/// Recycles a list of NetIncomingMessage instances for reuse; taking pressure off the garbage collector
/// </summary>
public void Recycle(IEnumerable<NetIncomingMessage> toRecycle)
{
if (m_incomingMessagesPool == null)
return;
// first recycle the storage of each message
if (m_storagePool != null)
{
lock (m_storagePool)
{
foreach (var msg in toRecycle)
{
var storage = msg.m_data;
msg.m_data = null;
m_storagePoolBytes += storage.Length;
int cnt = m_storagePool.Count;
for (int i = 0; i < cnt; i++)
{
if (m_storagePool[i] == null)
{
m_storagePool[i] = storage;
return;
}
}
msg.Reset();
m_storagePool.Add(storage);
}
}
}
// then recycle the message objects
m_incomingMessagesPool.Enqueue(toRecycle);
}
internal void Recycle(NetOutgoingMessage msg)
{
if (m_outgoingMessagesPool == null)
return;
#if DEBUG
if (m_outgoingMessagesPool.Contains(msg))
throw new NetException("Recyling already recycled message! Thread race?");
#endif
byte[] storage = msg.m_data;
msg.m_data = null;
// message fragments cannot be recycled
// TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them
if (msg.m_fragmentGroup == 0)
Recycle(storage);
msg.Reset();
m_outgoingMessagesPool.Enqueue(msg);
}
/// <summary>
/// Creates an incoming message with the required capacity for releasing to the application
/// </summary>
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, string text)
{
NetIncomingMessage retval;
if (string.IsNullOrEmpty(text))
{
retval = CreateIncomingMessage(tp, 1);
retval.Write(string.Empty);
return retval;
}
int numBytes = System.Text.Encoding.UTF8.GetByteCount(text);
retval = CreateIncomingMessage(tp, numBytes + (numBytes > 127 ? 2 : 1));
retval.Write(text);
return retval;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
public partial class NetPeer
{
private List<byte[]> m_storagePool; // sorted smallest to largest
private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool;
private NetQueue<NetIncomingMessage> m_incomingMessagesPool;
internal int m_storagePoolBytes;
private void InitializePools()
{
if (m_configuration.UseMessageRecycling)
{
m_storagePool = new List<byte[]>(16);
m_outgoingMessagesPool = new NetQueue<NetOutgoingMessage>(4);
m_incomingMessagesPool = new NetQueue<NetIncomingMessage>(4);
}
else
{
m_storagePool = null;
m_outgoingMessagesPool = null;
m_incomingMessagesPool = null;
}
}
internal byte[] GetStorage(int minimumCapacityInBytes)
{
if (m_storagePool == null)
return new byte[minimumCapacityInBytes];
lock (m_storagePool)
{
for (int i = 0; i < m_storagePool.Count; i++)
{
byte[] retval = m_storagePool[i];
if (retval != null && retval.Length >= minimumCapacityInBytes)
{
m_storagePool[i] = null;
m_storagePoolBytes -= retval.Length;
return retval;
}
}
}
m_statistics.m_bytesAllocated += minimumCapacityInBytes;
return new byte[minimumCapacityInBytes];
}
internal void Recycle(byte[] storage)
{
if (m_storagePool == null)
return;
lock (m_storagePool)
{
m_storagePoolBytes += storage.Length;
int cnt = m_storagePool.Count;
for (int i = 0; i < cnt; i++)
{
if (m_storagePool[i] == null)
{
m_storagePool[i] = storage;
return;
}
}
m_storagePool.Add(storage);
}
}
/// <summary>
/// Creates a new message for sending
/// </summary>
public NetOutgoingMessage CreateMessage()
{
return CreateMessage(m_configuration.m_defaultOutgoingMessageCapacity);
}
/// <summary>
/// Creates a new message for sending and writes the provided string to it
/// </summary>
public NetOutgoingMessage CreateMessage(string content)
{
byte[] bytes = Encoding.UTF8.GetBytes(content);
NetOutgoingMessage om = CreateMessage(2 + bytes.Length);
om.WriteVariableUInt32((uint)bytes.Length);
om.Write(bytes);
return om;
}
/// <summary>
/// Creates a new message for sending
/// </summary>
/// <param name="initialCapacity">initial capacity in bytes</param>
public NetOutgoingMessage CreateMessage(int initialCapacity)
{
NetOutgoingMessage retval;
if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval))
retval = new NetOutgoingMessage();
byte[] storage = GetStorage(initialCapacity);
retval.m_data = storage;
return retval;
}
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] useStorageData)
{
NetIncomingMessage retval;
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
retval = new NetIncomingMessage(tp);
else
retval.m_incomingMessageType = tp;
retval.m_data = useStorageData;
return retval;
}
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, int minimumByteSize)
{
NetIncomingMessage retval;
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
retval = new NetIncomingMessage(tp);
else
retval.m_incomingMessageType = tp;
retval.m_data = GetStorage(minimumByteSize);
return retval;
}
/// <summary>
/// Recycles a NetIncomingMessage instance for reuse; taking pressure off the garbage collector
/// </summary>
public void Recycle(NetIncomingMessage msg)
{
if (m_incomingMessagesPool == null)
return;
#if DEBUG
if (m_incomingMessagesPool.Contains(msg))
throw new NetException("Recyling already recycled message! Thread race?");
#endif
byte[] storage = msg.m_data;
msg.m_data = null;
Recycle(storage);
msg.Reset();
m_incomingMessagesPool.Enqueue(msg);
}
/// <summary>
/// Recycles a list of NetIncomingMessage instances for reuse; taking pressure off the garbage collector
/// </summary>
public void Recycle(IEnumerable<NetIncomingMessage> toRecycle)
{
if (m_incomingMessagesPool == null)
return;
// first recycle the storage of each message
if (m_storagePool != null)
{
lock (m_storagePool)
{
foreach (var msg in toRecycle)
{
var storage = msg.m_data;
msg.m_data = null;
m_storagePoolBytes += storage.Length;
int cnt = m_storagePool.Count;
for (int i = 0; i < cnt; i++)
{
if (m_storagePool[i] == null)
{
m_storagePool[i] = storage;
return;
}
}
msg.Reset();
m_storagePool.Add(storage);
}
}
}
// then recycle the message objects
m_incomingMessagesPool.Enqueue(toRecycle);
}
internal void Recycle(NetOutgoingMessage msg)
{
if (m_outgoingMessagesPool == null)
return;
#if DEBUG
if (m_outgoingMessagesPool.Contains(msg))
throw new NetException("Recyling already recycled message! Thread race?");
#endif
byte[] storage = msg.m_data;
msg.m_data = null;
// message fragments cannot be recycled
// TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them
if (msg.m_fragmentGroup == 0)
Recycle(storage);
msg.Reset();
m_outgoingMessagesPool.Enqueue(msg);
}
/// <summary>
/// Creates an incoming message with the required capacity for releasing to the application
/// </summary>
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, string text)
{
NetIncomingMessage retval;
if (string.IsNullOrEmpty(text))
{
retval = CreateIncomingMessage(tp, 1);
retval.Write(string.Empty);
return retval;
}
int numBytes = System.Text.Encoding.UTF8.GetByteCount(text);
retval = CreateIncomingMessage(tp, numBytes + (numBytes > 127 ? 2 : 1));
retval.Write(text);
return retval;
}
}
}

View File

@@ -1,226 +1,226 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Net;
namespace Lidgren.Network
{
public partial class NetPeer
{
/// <summary>
/// Send a message to a specific connection
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="recipient">The recipient connection</param>
/// <param name="method">How to deliver the message</param>
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method)
{
return SendMessage(msg, recipient, method, 0);
}
/// <summary>
/// Send a message to a specific connection
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="recipient">The recipient connection</param>
/// <param name="method">How to deliver the message</param>
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (recipient == null)
throw new ArgumentNullException("recipient");
if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod)
throw new ArgumentOutOfRangeException("sequenceChannel");
NetException.Assert(
((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) ||
((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)),
"Delivery method " + method + " cannot use sequence channels other than 0!"
);
NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
msg.m_isSent = true;
int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize
if (len <= recipient.m_currentMTU)
{
Interlocked.Increment(ref msg.m_recyclingCount);
return recipient.EnqueueMessage(msg, method, sequenceChannel);
}
else
{
// message must be fragmented!
if (recipient.m_status != NetConnectionStatus.Connected)
return NetSendResult.FailedNotConnected;
SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
return NetSendResult.Queued; // could be different for each connection; Queued is "most true"
}
}
internal int GetMTU(IList<NetConnection> recipients)
{
int count = recipients.Count;
NetException.Assert(count > 0);
int mtu = int.MaxValue;
for(int i=0;i<count;i++)
{
var conn = recipients[i];
int cmtu = conn.m_currentMTU;
if (cmtu < mtu)
mtu = cmtu;
}
return mtu;
}
/// <summary>
/// Send a message to a list of connections
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="recipients">The list of recipients to send to</param>
/// <param name="method">How to deliver the message</param>
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
public void SendMessage(NetOutgoingMessage msg, List<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (recipients == null)
throw new ArgumentNullException("recipients");
if (recipients.Count < 1)
throw new NetException("recipients must contain at least one item");
if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
int mtu = GetMTU(recipients);
msg.m_isSent = true;
int len = msg.GetEncodedSize();
if (len <= mtu)
{
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
foreach (NetConnection conn in recipients)
{
if (conn == null)
{
Interlocked.Decrement(ref msg.m_recyclingCount);
continue;
}
NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
if (res != NetSendResult.Queued && res != NetSendResult.Sent)
Interlocked.Decrement(ref msg.m_recyclingCount);
}
}
else
{
// message must be fragmented!
SendFragmentedMessage(msg, recipients, method, sequenceChannel);
}
return;
}
/// <summary>
/// Send a message to an unconnected host
/// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (host == null)
throw new ArgumentNullException("host");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
IPAddress adr = NetUtility.Resolve(host);
if (adr == null)
throw new NetException("Failed to resolve " + host);
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
Interlocked.Increment(ref msg.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(adr, port), msg));
}
/// <summary>
/// Send a message to an unconnected host
/// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (recipient == null)
throw new ArgumentNullException("recipient");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
Interlocked.Increment(ref msg.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
}
/// <summary>
/// Send a message to an unconnected host
/// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<IPEndPoint> recipients)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (recipients == null)
throw new ArgumentNullException("recipients");
if (recipients.Count < 1)
throw new NetException("recipients must contain at least one item");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
foreach(IPEndPoint ep in recipients)
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(ep, msg));
}
/// <summary>
/// Send a message to this exact same netpeer (loopback)
/// </summary>
public void SendUnconnectedToSelf(NetOutgoingMessage msg)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false)
return; // dropping unconnected message since it's not enabled for receiving
NetIncomingMessage om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, msg.LengthBytes);
om.m_isFragment = false;
om.m_receiveTime = NetTime.Now;
om.m_senderConnection = null;
om.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint;
om.m_bitLength = msg.LengthBits;
ReleaseMessage(om);
}
}
}
using System;
using System.Collections.Generic;
using System.Threading;
using System.Net;
namespace Lidgren.Network
{
public partial class NetPeer
{
/// <summary>
/// Send a message to a specific connection
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="recipient">The recipient connection</param>
/// <param name="method">How to deliver the message</param>
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method)
{
return SendMessage(msg, recipient, method, 0);
}
/// <summary>
/// Send a message to a specific connection
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="recipient">The recipient connection</param>
/// <param name="method">How to deliver the message</param>
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (recipient == null)
throw new ArgumentNullException("recipient");
if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod)
throw new ArgumentOutOfRangeException("sequenceChannel");
NetException.Assert(
((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) ||
((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)),
"Delivery method " + method + " cannot use sequence channels other than 0!"
);
NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
msg.m_isSent = true;
int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize
if (len <= recipient.m_currentMTU)
{
Interlocked.Increment(ref msg.m_recyclingCount);
return recipient.EnqueueMessage(msg, method, sequenceChannel);
}
else
{
// message must be fragmented!
if (recipient.m_status != NetConnectionStatus.Connected)
return NetSendResult.FailedNotConnected;
SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
return NetSendResult.Queued; // could be different for each connection; Queued is "most true"
}
}
internal int GetMTU(IList<NetConnection> recipients)
{
int count = recipients.Count;
NetException.Assert(count > 0);
int mtu = int.MaxValue;
for(int i=0;i<count;i++)
{
var conn = recipients[i];
int cmtu = conn.m_currentMTU;
if (cmtu < mtu)
mtu = cmtu;
}
return mtu;
}
/// <summary>
/// Send a message to a list of connections
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="recipients">The list of recipients to send to</param>
/// <param name="method">How to deliver the message</param>
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
public void SendMessage(NetOutgoingMessage msg, List<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (recipients == null)
throw new ArgumentNullException("recipients");
if (recipients.Count < 1)
throw new NetException("recipients must contain at least one item");
if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
int mtu = GetMTU(recipients);
msg.m_isSent = true;
int len = msg.GetEncodedSize();
if (len <= mtu)
{
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
foreach (NetConnection conn in recipients)
{
if (conn == null)
{
Interlocked.Decrement(ref msg.m_recyclingCount);
continue;
}
NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
if (res != NetSendResult.Queued && res != NetSendResult.Sent)
Interlocked.Decrement(ref msg.m_recyclingCount);
}
}
else
{
// message must be fragmented!
SendFragmentedMessage(msg, recipients, method, sequenceChannel);
}
return;
}
/// <summary>
/// Send a message to an unconnected host
/// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (host == null)
throw new ArgumentNullException("host");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
IPAddress adr = NetUtility.Resolve(host);
if (adr == null)
throw new NetException("Failed to resolve " + host);
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
Interlocked.Increment(ref msg.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(adr, port), msg));
}
/// <summary>
/// Send a message to an unconnected host
/// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (recipient == null)
throw new ArgumentNullException("recipient");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
Interlocked.Increment(ref msg.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
}
/// <summary>
/// Send a message to an unconnected host
/// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<IPEndPoint> recipients)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (recipients == null)
throw new ArgumentNullException("recipients");
if (recipients.Count < 1)
throw new NetException("recipients must contain at least one item");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
foreach(IPEndPoint ep in recipients)
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(ep, msg));
}
/// <summary>
/// Send a message to this exact same netpeer (loopback)
/// </summary>
public void SendUnconnectedToSelf(NetOutgoingMessage msg)
{
if (msg == null)
throw new ArgumentNullException("msg");
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false)
return; // dropping unconnected message since it's not enabled for receiving
NetIncomingMessage om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, msg.LengthBytes);
om.m_isFragment = false;
om.m_receiveTime = NetTime.Now;
om.m_senderConnection = null;
om.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint;
om.m_bitLength = msg.LengthBits;
ReleaseMessage(om);
}
}
}

View File

@@ -1,329 +1,329 @@
using System;
using System.Threading;
using System.Collections.Generic;
using System.Net;
namespace Lidgren.Network
{
/// <summary>
/// Represents a local peer capable of holding zero, one or more connections to remote peers
/// </summary>
public partial class NetPeer
{
private static int s_initializedPeersCount;
private int m_listenPort;
private object m_tag;
internal readonly List<NetConnection> m_connections;
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
private string m_shutdownReason;
/// <summary>
/// Gets the NetPeerStatus of the NetPeer
/// </summary>
public NetPeerStatus Status { get { return m_status; } }
/// <summary>
/// Signalling event which can be waited on to determine when a message is queued for reading.
/// Note that there is no guarantee that after the event is signaled the blocked thread will
/// find the message in the queue. Other user created threads could be preempted and dequeue
/// the message before the waiting thread wakes up.
/// </summary>
public AutoResetEvent MessageReceivedEvent { get { return m_messageReceivedEvent; } }
/// <summary>
/// Gets a unique identifier for this NetPeer based on Mac address and ip/port. Note! Not available until Start() has been called!
/// </summary>
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
/// <summary>
/// Gets the port number this NetPeer is listening and sending on, if Start() has been called
/// </summary>
public int Port { get { return m_listenPort; } }
/// <summary>
/// Returns an UPnP object if enabled in the NetPeerConfiguration
/// </summary>
public NetUPnP UPnP { get { return m_upnp; } }
/// <summary>
/// Gets or sets the application defined object containing data about the peer
/// </summary>
public object Tag
{
get { return m_tag; }
set { m_tag = value; }
}
/// <summary>
/// Gets a copy of the list of connections
/// </summary>
public List<NetConnection> Connections
{
get
{
lock (m_connections)
return new List<NetConnection>(m_connections);
}
}
/// <summary>
/// Gets the number of active connections
/// </summary>
public int ConnectionsCount
{
get { return m_connections.Count; }
}
/// <summary>
/// Statistics on this NetPeer since it was initialized
/// </summary>
public NetPeerStatistics Statistics
{
get { return m_statistics; }
}
/// <summary>
/// Gets the configuration used to instanciate this NetPeer
/// </summary>
public NetPeerConfiguration Configuration { get { return m_configuration; } }
/// <summary>
/// NetPeer constructor
/// </summary>
public NetPeer(NetPeerConfiguration config)
{
m_configuration = config;
m_statistics = new NetPeerStatistics(this);
m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(4);
m_unsentUnconnectedMessages = new NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>>(2);
m_connections = new List<NetConnection>();
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
m_handshakes = new Dictionary<IPEndPoint, NetConnection>();
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
m_status = NetPeerStatus.NotRunning;
m_receivedFragmentGroups = new Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>>();
}
/// <summary>
/// Binds to socket and spawns the networking thread
/// </summary>
public void Start()
{
if (m_status != NetPeerStatus.NotRunning)
{
// already running! Just ignore...
LogWarning("Start() called on already running NetPeer - ignoring.");
return;
}
m_status = NetPeerStatus.Starting;
// fix network thread name
if (m_configuration.NetworkThreadName == "Lidgren network thread")
{
int pc = Interlocked.Increment(ref s_initializedPeersCount);
m_configuration.NetworkThreadName = "Lidgren network thread " + pc.ToString();
}
InitializeNetwork();
// start network thread
m_networkThread = new Thread(new ThreadStart(NetworkLoop));
m_networkThread.Name = m_configuration.NetworkThreadName;
m_networkThread.IsBackground = true;
m_networkThread.Start();
// send upnp discovery
if (m_upnp != null)
m_upnp.Discover(this);
// allow some time for network thread to start up in case they call Connect() or UPnP calls immediately
Thread.Sleep(50);
}
/// <summary>
/// Get the connection, if any, for a certain remote endpoint
/// </summary>
public NetConnection GetConnection(IPEndPoint ep)
{
NetConnection retval;
// this should not pose a threading problem, m_connectionLookup is never added to concurrently
// and TryGetValue will not throw an exception on fail, only yield null, which is acceptable
m_connectionLookup.TryGetValue(ep, out retval);
return retval;
}
/// <summary>
/// Read a pending message from any connection, blocking up to maxMillis if needed
/// </summary>
public NetIncomingMessage WaitMessage(int maxMillis)
{
var msg = ReadMessage();
if (msg != null)
return msg; // no need to wait; we already have a message to deliver
if (m_messageReceivedEvent != null)
m_messageReceivedEvent.WaitOne(maxMillis);
return ReadMessage();
}
/// <summary>
/// Read a pending message from any connection, if any
/// </summary>
public NetIncomingMessage ReadMessage()
{
NetIncomingMessage retval;
if (m_releasedIncomingMessages.TryDequeue(out retval))
{
if (retval.MessageType == NetIncomingMessageType.StatusChanged)
{
NetConnectionStatus status = (NetConnectionStatus)retval.PeekByte();
retval.SenderConnection.m_visibleStatus = status;
}
}
return retval;
}
/// <summary>
/// Read a pending message from any connection, if any
/// </summary>
public int ReadMessages(IList<NetIncomingMessage> addTo)
{
int added = m_releasedIncomingMessages.TryDrain(addTo);
if (added > 0)
{
for (int i = 0; i < added; i++)
{
var index = addTo.Count - added + i;
var nim = addTo[index];
if (nim.MessageType == NetIncomingMessageType.StatusChanged)
{
NetConnectionStatus status = (NetConnectionStatus)nim.PeekByte();
nim.SenderConnection.m_visibleStatus = status;
}
}
}
return added;
}
// send message immediately
internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
{
VerifyNetworkThread();
NetException.Assert(msg.m_isSent == false);
bool connReset;
int len = msg.Encode(m_sendBuffer, 0, 0);
SendPacket(len, recipient, 1, out connReset);
}
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public NetConnection Connect(string host, int port)
{
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), null);
}
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage)
{
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), hailMessage);
}
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public NetConnection Connect(IPEndPoint remoteEndPoint)
{
return Connect(remoteEndPoint, null);
}
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public virtual NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
{
if (remoteEndPoint == null)
throw new ArgumentNullException("remoteEndPoint");
lock (m_connections)
{
if (m_status == NetPeerStatus.NotRunning)
throw new NetException("Must call Start() first");
if (m_connectionLookup.ContainsKey(remoteEndPoint))
throw new NetException("Already connected to that endpoint!");
NetConnection hs;
if (m_handshakes.TryGetValue(remoteEndPoint, out hs))
{
// already trying to connect to that endpoint; make another try
switch (hs.m_status)
{
case NetConnectionStatus.InitiatedConnect:
// send another connect
hs.m_connectRequested = true;
break;
case NetConnectionStatus.RespondedConnect:
// send another response
hs.SendConnectResponse((float)NetTime.Now, false);
break;
default:
// weird
LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.m_status);
break;
}
return hs;
}
NetConnection conn = new NetConnection(this, remoteEndPoint);
conn.m_status = NetConnectionStatus.InitiatedConnect;
conn.m_localHailMessage = hailMessage;
// handle on network thread
conn.m_connectRequested = true;
conn.m_connectionInitiator = true;
m_handshakes.Add(remoteEndPoint, conn);
return conn;
}
}
/// <summary>
/// Send raw bytes; only used for debugging
/// </summary>
#if DEBUG
public void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
#else
internal void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
#endif
{
// wrong thread - this miiiight crash with network thread... but what's a boy to do.
Array.Copy(arr, offset, m_sendBuffer, 0, length);
bool unused;
SendPacket(length, destination, 1, out unused);
}
/// <summary>
/// Disconnects all active connections and closes the socket
/// </summary>
public void Shutdown(string bye)
{
// called on user thread
if (m_socket == null)
return; // already shut down
LogDebug("Shutdown requested");
m_shutdownReason = bye;
m_status = NetPeerStatus.ShutdownRequested;
}
}
}
using System;
using System.Threading;
using System.Collections.Generic;
using System.Net;
namespace Lidgren.Network
{
/// <summary>
/// Represents a local peer capable of holding zero, one or more connections to remote peers
/// </summary>
public partial class NetPeer
{
private static int s_initializedPeersCount;
private int m_listenPort;
private object m_tag;
internal readonly List<NetConnection> m_connections;
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
private string m_shutdownReason;
/// <summary>
/// Gets the NetPeerStatus of the NetPeer
/// </summary>
public NetPeerStatus Status { get { return m_status; } }
/// <summary>
/// Signalling event which can be waited on to determine when a message is queued for reading.
/// Note that there is no guarantee that after the event is signaled the blocked thread will
/// find the message in the queue. Other user created threads could be preempted and dequeue
/// the message before the waiting thread wakes up.
/// </summary>
public AutoResetEvent MessageReceivedEvent { get { return m_messageReceivedEvent; } }
/// <summary>
/// Gets a unique identifier for this NetPeer based on Mac address and ip/port. Note! Not available until Start() has been called!
/// </summary>
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
/// <summary>
/// Gets the port number this NetPeer is listening and sending on, if Start() has been called
/// </summary>
public int Port { get { return m_listenPort; } }
/// <summary>
/// Returns an UPnP object if enabled in the NetPeerConfiguration
/// </summary>
public NetUPnP UPnP { get { return m_upnp; } }
/// <summary>
/// Gets or sets the application defined object containing data about the peer
/// </summary>
public object Tag
{
get { return m_tag; }
set { m_tag = value; }
}
/// <summary>
/// Gets a copy of the list of connections
/// </summary>
public List<NetConnection> Connections
{
get
{
lock (m_connections)
return new List<NetConnection>(m_connections);
}
}
/// <summary>
/// Gets the number of active connections
/// </summary>
public int ConnectionsCount
{
get { return m_connections.Count; }
}
/// <summary>
/// Statistics on this NetPeer since it was initialized
/// </summary>
public NetPeerStatistics Statistics
{
get { return m_statistics; }
}
/// <summary>
/// Gets the configuration used to instanciate this NetPeer
/// </summary>
public NetPeerConfiguration Configuration { get { return m_configuration; } }
/// <summary>
/// NetPeer constructor
/// </summary>
public NetPeer(NetPeerConfiguration config)
{
m_configuration = config;
m_statistics = new NetPeerStatistics(this);
m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(4);
m_unsentUnconnectedMessages = new NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>>(2);
m_connections = new List<NetConnection>();
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
m_handshakes = new Dictionary<IPEndPoint, NetConnection>();
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
m_status = NetPeerStatus.NotRunning;
m_receivedFragmentGroups = new Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>>();
}
/// <summary>
/// Binds to socket and spawns the networking thread
/// </summary>
public void Start()
{
if (m_status != NetPeerStatus.NotRunning)
{
// already running! Just ignore...
LogWarning("Start() called on already running NetPeer - ignoring.");
return;
}
m_status = NetPeerStatus.Starting;
// fix network thread name
if (m_configuration.NetworkThreadName == "Lidgren network thread")
{
int pc = Interlocked.Increment(ref s_initializedPeersCount);
m_configuration.NetworkThreadName = "Lidgren network thread " + pc.ToString();
}
InitializeNetwork();
// start network thread
m_networkThread = new Thread(new ThreadStart(NetworkLoop));
m_networkThread.Name = m_configuration.NetworkThreadName;
m_networkThread.IsBackground = true;
m_networkThread.Start();
// send upnp discovery
if (m_upnp != null)
m_upnp.Discover(this);
// allow some time for network thread to start up in case they call Connect() or UPnP calls immediately
Thread.Sleep(50);
}
/// <summary>
/// Get the connection, if any, for a certain remote endpoint
/// </summary>
public NetConnection GetConnection(IPEndPoint ep)
{
NetConnection retval;
// this should not pose a threading problem, m_connectionLookup is never added to concurrently
// and TryGetValue will not throw an exception on fail, only yield null, which is acceptable
m_connectionLookup.TryGetValue(ep, out retval);
return retval;
}
/// <summary>
/// Read a pending message from any connection, blocking up to maxMillis if needed
/// </summary>
public NetIncomingMessage WaitMessage(int maxMillis)
{
var msg = ReadMessage();
if (msg != null)
return msg; // no need to wait; we already have a message to deliver
if (m_messageReceivedEvent != null)
m_messageReceivedEvent.WaitOne(maxMillis);
return ReadMessage();
}
/// <summary>
/// Read a pending message from any connection, if any
/// </summary>
public NetIncomingMessage ReadMessage()
{
NetIncomingMessage retval;
if (m_releasedIncomingMessages.TryDequeue(out retval))
{
if (retval.MessageType == NetIncomingMessageType.StatusChanged)
{
NetConnectionStatus status = (NetConnectionStatus)retval.PeekByte();
retval.SenderConnection.m_visibleStatus = status;
}
}
return retval;
}
/// <summary>
/// Read a pending message from any connection, if any
/// </summary>
public int ReadMessages(IList<NetIncomingMessage> addTo)
{
int added = m_releasedIncomingMessages.TryDrain(addTo);
if (added > 0)
{
for (int i = 0; i < added; i++)
{
var index = addTo.Count - added + i;
var nim = addTo[index];
if (nim.MessageType == NetIncomingMessageType.StatusChanged)
{
NetConnectionStatus status = (NetConnectionStatus)nim.PeekByte();
nim.SenderConnection.m_visibleStatus = status;
}
}
}
return added;
}
// send message immediately
internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
{
VerifyNetworkThread();
NetException.Assert(msg.m_isSent == false);
bool connReset;
int len = msg.Encode(m_sendBuffer, 0, 0);
SendPacket(len, recipient, 1, out connReset);
}
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public NetConnection Connect(string host, int port)
{
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), null);
}
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage)
{
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), hailMessage);
}
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public NetConnection Connect(IPEndPoint remoteEndPoint)
{
return Connect(remoteEndPoint, null);
}
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public virtual NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
{
if (remoteEndPoint == null)
throw new ArgumentNullException("remoteEndPoint");
lock (m_connections)
{
if (m_status == NetPeerStatus.NotRunning)
throw new NetException("Must call Start() first");
if (m_connectionLookup.ContainsKey(remoteEndPoint))
throw new NetException("Already connected to that endpoint!");
NetConnection hs;
if (m_handshakes.TryGetValue(remoteEndPoint, out hs))
{
// already trying to connect to that endpoint; make another try
switch (hs.m_status)
{
case NetConnectionStatus.InitiatedConnect:
// send another connect
hs.m_connectRequested = true;
break;
case NetConnectionStatus.RespondedConnect:
// send another response
hs.SendConnectResponse((float)NetTime.Now, false);
break;
default:
// weird
LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.m_status);
break;
}
return hs;
}
NetConnection conn = new NetConnection(this, remoteEndPoint);
conn.m_status = NetConnectionStatus.InitiatedConnect;
conn.m_localHailMessage = hailMessage;
// handle on network thread
conn.m_connectRequested = true;
conn.m_connectionInitiator = true;
m_handshakes.Add(remoteEndPoint, conn);
return conn;
}
}
/// <summary>
/// Send raw bytes; only used for debugging
/// </summary>
#if DEBUG
public void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
#else
internal void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
#endif
{
// wrong thread - this miiiight crash with network thread... but what's a boy to do.
Array.Copy(arr, offset, m_sendBuffer, 0, length);
bool unused;
SendPacket(length, destination, 1, out unused);
}
/// <summary>
/// Disconnects all active connections and closes the socket
/// </summary>
public void Shutdown(string bye)
{
// called on user thread
if (m_socket == null)
return; // already shut down
LogDebug("Shutdown requested");
m_shutdownReason = bye;
m_status = NetPeerStatus.ShutdownRequested;
}
}
}

View File

@@ -1,471 +1,471 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Net;
namespace Lidgren.Network
{
/// <summary>
/// Partly immutable after NetPeer has been initialized
/// </summary>
public sealed class NetPeerConfiguration
{
private const string c_isLockedMessage = "You may not modify the NetPeerConfiguration after it has been used to initialize a NetPeer";
private bool m_isLocked;
private readonly string m_appIdentifier;
private string m_networkThreadName;
private IPAddress m_localAddress;
private IPAddress m_broadcastAddress;
internal bool m_acceptIncomingConnections;
internal int m_maximumConnections;
internal int m_defaultOutgoingMessageCapacity;
internal float m_pingInterval;
internal bool m_useMessageRecycling;
internal float m_connectionTimeout;
internal bool m_enableUPnP;
internal bool m_autoFlushSendQueue;
internal NetIncomingMessageType m_disabledTypes;
internal int m_port;
internal int m_receiveBufferSize;
internal int m_sendBufferSize;
internal float m_resendHandshakeInterval;
internal int m_maximumHandshakeAttempts;
// bad network simulation
internal float m_loss;
internal float m_duplicates;
internal float m_minimumOneWayLatency;
internal float m_randomOneWayLatency;
// MTU
internal int m_maximumTransmissionUnit;
internal bool m_autoExpandMTU;
internal float m_expandMTUFrequency;
internal int m_expandMTUFailAttempts;
/// <summary>
/// NetPeerConfiguration constructor
/// </summary>
public NetPeerConfiguration(string appIdentifier)
{
if (string.IsNullOrEmpty(appIdentifier))
throw new NetException("App identifier must be at least one character long");
m_appIdentifier = appIdentifier.ToString(System.Globalization.CultureInfo.InvariantCulture);
//
// default values
//
m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage | NetIncomingMessageType.ConnectionLatencyUpdated;
m_networkThreadName = "Lidgren network thread";
m_localAddress = IPAddress.Any;
m_broadcastAddress = IPAddress.Broadcast;
var ip = NetUtility.GetBroadcastAddress();
if (ip != null)
{
m_broadcastAddress = ip;
}
m_port = 0;
m_receiveBufferSize = 131071;
m_sendBufferSize = 131071;
m_acceptIncomingConnections = false;
m_maximumConnections = 32;
m_defaultOutgoingMessageCapacity = 16;
m_pingInterval = 4.0f;
m_connectionTimeout = 25.0f;
m_useMessageRecycling = true;
m_resendHandshakeInterval = 3.0f;
m_maximumHandshakeAttempts = 5;
m_autoFlushSendQueue = true;
// Maximum transmission unit
// Ethernet can take 1500 bytes of payload, so lets stay below that.
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
// -20 bytes IP header
// -8 bytes UDP header
// -4 bytes to be on the safe side and align to 8-byte boundary
// Total 1408 bytes
// Note that lidgren headers (5 bytes) are not included here; since it's part of the "mtu payload"
m_maximumTransmissionUnit = 1408;
m_autoExpandMTU = false;
m_expandMTUFrequency = 2.0f;
m_expandMTUFailAttempts = 5;
m_loss = 0.0f;
m_minimumOneWayLatency = 0.0f;
m_randomOneWayLatency = 0.0f;
m_duplicates = 0.0f;
m_isLocked = false;
}
internal void Lock()
{
m_isLocked = true;
}
/// <summary>
/// Gets the identifier of this application; the library can only connect to matching app identifier peers
/// </summary>
public string AppIdentifier
{
get { return m_appIdentifier; }
}
/// <summary>
/// Enables receiving of the specified type of message
/// </summary>
public void EnableMessageType(NetIncomingMessageType type)
{
m_disabledTypes &= (~type);
}
/// <summary>
/// Disables receiving of the specified type of message
/// </summary>
public void DisableMessageType(NetIncomingMessageType type)
{
m_disabledTypes |= type;
}
/// <summary>
/// Enables or disables receiving of the specified type of message
/// </summary>
public void SetMessageTypeEnabled(NetIncomingMessageType type, bool enabled)
{
if (enabled)
m_disabledTypes &= (~type);
else
m_disabledTypes |= type;
}
/// <summary>
/// Gets if receiving of the specified type of message is enabled
/// </summary>
public bool IsMessageTypeEnabled(NetIncomingMessageType type)
{
return !((m_disabledTypes & type) == type);
}
/// <summary>
/// Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized.
/// </summary>
public string NetworkThreadName
{
get { return m_networkThreadName; }
set
{
if (m_isLocked)
throw new NetException("NetworkThreadName may not be set after the NetPeer which uses the configuration has been started");
m_networkThreadName = value;
}
}
/// <summary>
/// Gets or sets the maximum amount of connections this peer can hold. Cannot be changed once NetPeer is initialized.
/// </summary>
public int MaximumConnections
{
get { return m_maximumConnections; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_maximumConnections = value;
}
}
/// <summary>
/// Gets or sets the maximum amount of bytes to send in a single packet, excluding ip, udp and lidgren headers. Cannot be changed once NetPeer is initialized.
/// </summary>
public int MaximumTransmissionUnit
{
get { return m_maximumTransmissionUnit; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
if (value < 1 || value >= ((ushort.MaxValue + 1) / 8))
throw new NetException("MaximumTransmissionUnit must be between 1 and " + (((ushort.MaxValue + 1) / 8) - 1) + " bytes");
m_maximumTransmissionUnit = value;
}
}
/// <summary>
/// Gets or sets the default capacity in bytes when NetPeer.CreateMessage() is called without argument
/// </summary>
public int DefaultOutgoingMessageCapacity
{
get { return m_defaultOutgoingMessageCapacity; }
set { m_defaultOutgoingMessageCapacity = value; }
}
/// <summary>
/// Gets or sets the time between latency calculating pings
/// </summary>
public float PingInterval
{
get { return m_pingInterval; }
set { m_pingInterval = value; }
}
/// <summary>
/// Gets or sets if the library should recycling messages to avoid excessive garbage collection. Cannot be changed once NetPeer is initialized.
/// </summary>
public bool UseMessageRecycling
{
get { return m_useMessageRecycling; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_useMessageRecycling = value;
}
}
/// <summary>
/// Gets or sets the number of seconds timeout will be postponed on a successful ping/pong
/// </summary>
public float ConnectionTimeout
{
get { return m_connectionTimeout; }
set
{
if (value < m_pingInterval)
throw new NetException("Connection timeout cannot be lower than ping interval!");
m_connectionTimeout = value;
}
}
/// <summary>
/// Enables UPnP support; enabling port forwarding and getting external ip
/// </summary>
public bool EnableUPnP
{
get { return m_enableUPnP; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_enableUPnP = value;
}
}
/// <summary>
/// Enables or disables automatic flushing of the send queue. If disabled, you must manully call NetPeer.FlushSendQueue() to flush sent messages to network.
/// </summary>
public bool AutoFlushSendQueue
{
get { return m_autoFlushSendQueue; }
set { m_autoFlushSendQueue = value; }
}
/// <summary>
/// Gets or sets the local ip address to bind to. Defaults to IPAddress.Any. Cannot be changed once NetPeer is initialized.
/// </summary>
public IPAddress LocalAddress
{
get { return m_localAddress; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_localAddress = value;
}
}
/// <summary>
/// Gets or sets the local broadcast address to use when broadcasting
/// </summary>
public IPAddress BroadcastAddress
{
get { return m_broadcastAddress; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_broadcastAddress = value;
}
}
/// <summary>
/// Gets or sets the local port to bind to. Defaults to 0. Cannot be changed once NetPeer is initialized.
/// </summary>
public int Port
{
get { return m_port; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_port = value;
}
}
/// <summary>
/// Gets or sets the size in bytes of the receiving buffer. Defaults to 131071 bytes. Cannot be changed once NetPeer is initialized.
/// </summary>
public int ReceiveBufferSize
{
get { return m_receiveBufferSize; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_receiveBufferSize = value;
}
}
/// <summary>
/// Gets or sets the size in bytes of the sending buffer. Defaults to 131071 bytes. Cannot be changed once NetPeer is initialized.
/// </summary>
public int SendBufferSize
{
get { return m_sendBufferSize; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_sendBufferSize = value;
}
}
/// <summary>
/// Gets or sets if the NetPeer should accept incoming connections. This is automatically set to true in NetServer and false in NetClient.
/// </summary>
public bool AcceptIncomingConnections
{
get { return m_acceptIncomingConnections; }
set { m_acceptIncomingConnections = value; }
}
/// <summary>
/// Gets or sets the number of seconds between handshake attempts
/// </summary>
public float ResendHandshakeInterval
{
get { return m_resendHandshakeInterval; }
set { m_resendHandshakeInterval = value; }
}
/// <summary>
/// Gets or sets the maximum number of handshake attempts before failing to connect
/// </summary>
public int MaximumHandshakeAttempts
{
get { return m_maximumHandshakeAttempts; }
set
{
if (value < 1)
throw new NetException("MaximumHandshakeAttempts must be at least 1");
m_maximumHandshakeAttempts = value;
}
}
/// <summary>
/// Gets or sets if the NetPeer should send large messages to try to expand the maximum transmission unit size
/// </summary>
public bool AutoExpandMTU
{
get { return m_autoExpandMTU; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_autoExpandMTU = value;
}
}
/// <summary>
/// Gets or sets how often to send large messages to expand MTU if AutoExpandMTU is enabled
/// </summary>
public float ExpandMTUFrequency
{
get { return m_expandMTUFrequency; }
set { m_expandMTUFrequency = value; }
}
/// <summary>
/// Gets or sets the number of failed expand mtu attempts to perform before setting final MTU
/// </summary>
public int ExpandMTUFailAttempts
{
get { return m_expandMTUFailAttempts; }
set { m_expandMTUFailAttempts = value; }
}
#if DEBUG
/// <summary>
/// Gets or sets the simulated amount of sent packets lost from 0.0f to 1.0f
/// </summary>
public float SimulatedLoss
{
get { return m_loss; }
set { m_loss = value; }
}
/// <summary>
/// Gets or sets the minimum simulated amount of one way latency for sent packets in seconds
/// </summary>
public float SimulatedMinimumLatency
{
get { return m_minimumOneWayLatency; }
set { m_minimumOneWayLatency = value; }
}
/// <summary>
/// Gets or sets the simulated added random amount of one way latency for sent packets in seconds
/// </summary>
public float SimulatedRandomLatency
{
get { return m_randomOneWayLatency; }
set { m_randomOneWayLatency = value; }
}
/// <summary>
/// Gets the average simulated one way latency in seconds
/// </summary>
public float SimulatedAverageLatency
{
get { return m_minimumOneWayLatency + (m_randomOneWayLatency * 0.5f); }
}
/// <summary>
/// Gets or sets the simulated amount of duplicated packets from 0.0f to 1.0f
/// </summary>
public float SimulatedDuplicatesChance
{
get { return m_duplicates; }
set { m_duplicates = value; }
}
#endif
/// <summary>
/// Creates a memberwise shallow clone of this configuration
/// </summary>
public NetPeerConfiguration Clone()
{
NetPeerConfiguration retval = this.MemberwiseClone() as NetPeerConfiguration;
retval.m_isLocked = false;
return retval;
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Net;
namespace Lidgren.Network
{
/// <summary>
/// Partly immutable after NetPeer has been initialized
/// </summary>
public sealed class NetPeerConfiguration
{
private const string c_isLockedMessage = "You may not modify the NetPeerConfiguration after it has been used to initialize a NetPeer";
private bool m_isLocked;
private readonly string m_appIdentifier;
private string m_networkThreadName;
private IPAddress m_localAddress;
private IPAddress m_broadcastAddress;
internal bool m_acceptIncomingConnections;
internal int m_maximumConnections;
internal int m_defaultOutgoingMessageCapacity;
internal float m_pingInterval;
internal bool m_useMessageRecycling;
internal float m_connectionTimeout;
internal bool m_enableUPnP;
internal bool m_autoFlushSendQueue;
internal NetIncomingMessageType m_disabledTypes;
internal int m_port;
internal int m_receiveBufferSize;
internal int m_sendBufferSize;
internal float m_resendHandshakeInterval;
internal int m_maximumHandshakeAttempts;
// bad network simulation
internal float m_loss;
internal float m_duplicates;
internal float m_minimumOneWayLatency;
internal float m_randomOneWayLatency;
// MTU
internal int m_maximumTransmissionUnit;
internal bool m_autoExpandMTU;
internal float m_expandMTUFrequency;
internal int m_expandMTUFailAttempts;
/// <summary>
/// NetPeerConfiguration constructor
/// </summary>
public NetPeerConfiguration(string appIdentifier)
{
if (string.IsNullOrEmpty(appIdentifier))
throw new NetException("App identifier must be at least one character long");
m_appIdentifier = appIdentifier.ToString(System.Globalization.CultureInfo.InvariantCulture);
//
// default values
//
m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage | NetIncomingMessageType.ConnectionLatencyUpdated;
m_networkThreadName = "Lidgren network thread";
m_localAddress = IPAddress.Any;
m_broadcastAddress = IPAddress.Broadcast;
var ip = NetUtility.GetBroadcastAddress();
if (ip != null)
{
m_broadcastAddress = ip;
}
m_port = 0;
m_receiveBufferSize = 131071;
m_sendBufferSize = 131071;
m_acceptIncomingConnections = false;
m_maximumConnections = 32;
m_defaultOutgoingMessageCapacity = 16;
m_pingInterval = 4.0f;
m_connectionTimeout = 25.0f;
m_useMessageRecycling = true;
m_resendHandshakeInterval = 3.0f;
m_maximumHandshakeAttempts = 5;
m_autoFlushSendQueue = true;
// Maximum transmission unit
// Ethernet can take 1500 bytes of payload, so lets stay below that.
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
// -20 bytes IP header
// -8 bytes UDP header
// -4 bytes to be on the safe side and align to 8-byte boundary
// Total 1408 bytes
// Note that lidgren headers (5 bytes) are not included here; since it's part of the "mtu payload"
m_maximumTransmissionUnit = 1408;
m_autoExpandMTU = false;
m_expandMTUFrequency = 2.0f;
m_expandMTUFailAttempts = 5;
m_loss = 0.0f;
m_minimumOneWayLatency = 0.0f;
m_randomOneWayLatency = 0.0f;
m_duplicates = 0.0f;
m_isLocked = false;
}
internal void Lock()
{
m_isLocked = true;
}
/// <summary>
/// Gets the identifier of this application; the library can only connect to matching app identifier peers
/// </summary>
public string AppIdentifier
{
get { return m_appIdentifier; }
}
/// <summary>
/// Enables receiving of the specified type of message
/// </summary>
public void EnableMessageType(NetIncomingMessageType type)
{
m_disabledTypes &= (~type);
}
/// <summary>
/// Disables receiving of the specified type of message
/// </summary>
public void DisableMessageType(NetIncomingMessageType type)
{
m_disabledTypes |= type;
}
/// <summary>
/// Enables or disables receiving of the specified type of message
/// </summary>
public void SetMessageTypeEnabled(NetIncomingMessageType type, bool enabled)
{
if (enabled)
m_disabledTypes &= (~type);
else
m_disabledTypes |= type;
}
/// <summary>
/// Gets if receiving of the specified type of message is enabled
/// </summary>
public bool IsMessageTypeEnabled(NetIncomingMessageType type)
{
return !((m_disabledTypes & type) == type);
}
/// <summary>
/// Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized.
/// </summary>
public string NetworkThreadName
{
get { return m_networkThreadName; }
set
{
if (m_isLocked)
throw new NetException("NetworkThreadName may not be set after the NetPeer which uses the configuration has been started");
m_networkThreadName = value;
}
}
/// <summary>
/// Gets or sets the maximum amount of connections this peer can hold. Cannot be changed once NetPeer is initialized.
/// </summary>
public int MaximumConnections
{
get { return m_maximumConnections; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_maximumConnections = value;
}
}
/// <summary>
/// Gets or sets the maximum amount of bytes to send in a single packet, excluding ip, udp and lidgren headers. Cannot be changed once NetPeer is initialized.
/// </summary>
public int MaximumTransmissionUnit
{
get { return m_maximumTransmissionUnit; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
if (value < 1 || value >= ((ushort.MaxValue + 1) / 8))
throw new NetException("MaximumTransmissionUnit must be between 1 and " + (((ushort.MaxValue + 1) / 8) - 1) + " bytes");
m_maximumTransmissionUnit = value;
}
}
/// <summary>
/// Gets or sets the default capacity in bytes when NetPeer.CreateMessage() is called without argument
/// </summary>
public int DefaultOutgoingMessageCapacity
{
get { return m_defaultOutgoingMessageCapacity; }
set { m_defaultOutgoingMessageCapacity = value; }
}
/// <summary>
/// Gets or sets the time between latency calculating pings
/// </summary>
public float PingInterval
{
get { return m_pingInterval; }
set { m_pingInterval = value; }
}
/// <summary>
/// Gets or sets if the library should recycling messages to avoid excessive garbage collection. Cannot be changed once NetPeer is initialized.
/// </summary>
public bool UseMessageRecycling
{
get { return m_useMessageRecycling; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_useMessageRecycling = value;
}
}
/// <summary>
/// Gets or sets the number of seconds timeout will be postponed on a successful ping/pong
/// </summary>
public float ConnectionTimeout
{
get { return m_connectionTimeout; }
set
{
if (value < m_pingInterval)
throw new NetException("Connection timeout cannot be lower than ping interval!");
m_connectionTimeout = value;
}
}
/// <summary>
/// Enables UPnP support; enabling port forwarding and getting external ip
/// </summary>
public bool EnableUPnP
{
get { return m_enableUPnP; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_enableUPnP = value;
}
}
/// <summary>
/// Enables or disables automatic flushing of the send queue. If disabled, you must manully call NetPeer.FlushSendQueue() to flush sent messages to network.
/// </summary>
public bool AutoFlushSendQueue
{
get { return m_autoFlushSendQueue; }
set { m_autoFlushSendQueue = value; }
}
/// <summary>
/// Gets or sets the local ip address to bind to. Defaults to IPAddress.Any. Cannot be changed once NetPeer is initialized.
/// </summary>
public IPAddress LocalAddress
{
get { return m_localAddress; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_localAddress = value;
}
}
/// <summary>
/// Gets or sets the local broadcast address to use when broadcasting
/// </summary>
public IPAddress BroadcastAddress
{
get { return m_broadcastAddress; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_broadcastAddress = value;
}
}
/// <summary>
/// Gets or sets the local port to bind to. Defaults to 0. Cannot be changed once NetPeer is initialized.
/// </summary>
public int Port
{
get { return m_port; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_port = value;
}
}
/// <summary>
/// Gets or sets the size in bytes of the receiving buffer. Defaults to 131071 bytes. Cannot be changed once NetPeer is initialized.
/// </summary>
public int ReceiveBufferSize
{
get { return m_receiveBufferSize; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_receiveBufferSize = value;
}
}
/// <summary>
/// Gets or sets the size in bytes of the sending buffer. Defaults to 131071 bytes. Cannot be changed once NetPeer is initialized.
/// </summary>
public int SendBufferSize
{
get { return m_sendBufferSize; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_sendBufferSize = value;
}
}
/// <summary>
/// Gets or sets if the NetPeer should accept incoming connections. This is automatically set to true in NetServer and false in NetClient.
/// </summary>
public bool AcceptIncomingConnections
{
get { return m_acceptIncomingConnections; }
set { m_acceptIncomingConnections = value; }
}
/// <summary>
/// Gets or sets the number of seconds between handshake attempts
/// </summary>
public float ResendHandshakeInterval
{
get { return m_resendHandshakeInterval; }
set { m_resendHandshakeInterval = value; }
}
/// <summary>
/// Gets or sets the maximum number of handshake attempts before failing to connect
/// </summary>
public int MaximumHandshakeAttempts
{
get { return m_maximumHandshakeAttempts; }
set
{
if (value < 1)
throw new NetException("MaximumHandshakeAttempts must be at least 1");
m_maximumHandshakeAttempts = value;
}
}
/// <summary>
/// Gets or sets if the NetPeer should send large messages to try to expand the maximum transmission unit size
/// </summary>
public bool AutoExpandMTU
{
get { return m_autoExpandMTU; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_autoExpandMTU = value;
}
}
/// <summary>
/// Gets or sets how often to send large messages to expand MTU if AutoExpandMTU is enabled
/// </summary>
public float ExpandMTUFrequency
{
get { return m_expandMTUFrequency; }
set { m_expandMTUFrequency = value; }
}
/// <summary>
/// Gets or sets the number of failed expand mtu attempts to perform before setting final MTU
/// </summary>
public int ExpandMTUFailAttempts
{
get { return m_expandMTUFailAttempts; }
set { m_expandMTUFailAttempts = value; }
}
#if DEBUG
/// <summary>
/// Gets or sets the simulated amount of sent packets lost from 0.0f to 1.0f
/// </summary>
public float SimulatedLoss
{
get { return m_loss; }
set { m_loss = value; }
}
/// <summary>
/// Gets or sets the minimum simulated amount of one way latency for sent packets in seconds
/// </summary>
public float SimulatedMinimumLatency
{
get { return m_minimumOneWayLatency; }
set { m_minimumOneWayLatency = value; }
}
/// <summary>
/// Gets or sets the simulated added random amount of one way latency for sent packets in seconds
/// </summary>
public float SimulatedRandomLatency
{
get { return m_randomOneWayLatency; }
set { m_randomOneWayLatency = value; }
}
/// <summary>
/// Gets the average simulated one way latency in seconds
/// </summary>
public float SimulatedAverageLatency
{
get { return m_minimumOneWayLatency + (m_randomOneWayLatency * 0.5f); }
}
/// <summary>
/// Gets or sets the simulated amount of duplicated packets from 0.0f to 1.0f
/// </summary>
public float SimulatedDuplicatesChance
{
get { return m_duplicates; }
set { m_duplicates = value; }
}
#endif
/// <summary>
/// Creates a memberwise shallow clone of this configuration
/// </summary>
public NetPeerConfiguration Clone()
{
NetPeerConfiguration retval = this.MemberwiseClone() as NetPeerConfiguration;
retval.m_isLocked = false;
return retval;
}
}
}

View File

@@ -1,161 +1,161 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Uncomment the line below to get statistics in RELEASE builds
#define USE_RELEASE_STATISTICS
using System;
using System.Text;
using System.Diagnostics;
namespace Lidgren.Network
{
/// <summary>
/// Statistics for a NetPeer instance
/// </summary>
public sealed class NetPeerStatistics
{
private readonly NetPeer m_peer;
internal int m_sentPackets;
internal int m_receivedPackets;
internal int m_sentMessages;
internal int m_receivedMessages;
internal int m_sentBytes;
internal int m_receivedBytes;
internal long m_bytesAllocated;
internal NetPeerStatistics(NetPeer peer)
{
m_peer = peer;
Reset();
}
internal void Reset()
{
m_sentPackets = 0;
m_receivedPackets = 0;
m_sentMessages = 0;
m_receivedMessages = 0;
m_sentBytes = 0;
m_receivedBytes = 0;
m_bytesAllocated = 0;
}
/// <summary>
/// Gets the number of sent packets since the NetPeer was initialized
/// </summary>
public int SentPackets { get { return m_sentPackets; } }
/// <summary>
/// Gets the number of received packets since the NetPeer was initialized
/// </summary>
public int ReceivedPackets { get { return m_receivedPackets; } }
/// <summary>
/// Gets the number of sent messages since the NetPeer was initialized
/// </summary>
public int SentMessages { get { return m_sentMessages; } }
/// <summary>
/// Gets the number of received messages since the NetPeer was initialized
/// </summary>
public int ReceivedMessages { get { return m_receivedMessages; } }
/// <summary>
/// Gets the number of sent bytes since the NetPeer was initialized
/// </summary>
public int SentBytes { get { return m_sentBytes; } }
/// <summary>
/// Gets the number of received bytes since the NetPeer was initialized
/// </summary>
public int ReceivedBytes { get { return m_receivedBytes; } }
/// <summary>
/// Gets the number of bytes allocated (and possibly garbage collected) for message storage
/// </summary>
public long StorageBytesAllocated { get { return m_bytesAllocated; } }
/// <summary>
/// Gets the number of bytes in the recycled pool
/// </summary>
public int BytesInRecyclePool { get { return m_peer.m_storagePoolBytes; } }
#if USE_RELEASE_STATISTICS
internal void PacketSent(int numBytes, int numMessages)
{
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#else
[Conditional("DEBUG")]
internal void PacketSent(int numBytes, int numMessages)
{
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS
internal void PacketReceived(int numBytes, int numMessages)
{
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#else
[Conditional("DEBUG")]
internal void PacketReceived(int numBytes, int numMessages)
{
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#endif
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
StringBuilder bdr = new StringBuilder();
bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections");
#if DEBUG || USE_RELEASE_STATISTICS
bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets");
bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages in " + m_receivedPackets + " packets");
#else
bdr.AppendLine("Sent (n/a) bytes in (n/a) messages in (n/a) packets");
bdr.AppendLine("Received (n/a) bytes in (n/a) messages in (n/a) packets");
#endif
bdr.AppendLine("Storage allocated " + m_bytesAllocated + " bytes");
bdr.AppendLine("Recycled pool " + m_peer.m_storagePoolBytes + " bytes");
return bdr.ToString();
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Uncomment the line below to get statistics in RELEASE builds
#define USE_RELEASE_STATISTICS
using System;
using System.Text;
using System.Diagnostics;
namespace Lidgren.Network
{
/// <summary>
/// Statistics for a NetPeer instance
/// </summary>
public sealed class NetPeerStatistics
{
private readonly NetPeer m_peer;
internal int m_sentPackets;
internal int m_receivedPackets;
internal int m_sentMessages;
internal int m_receivedMessages;
internal int m_sentBytes;
internal int m_receivedBytes;
internal long m_bytesAllocated;
internal NetPeerStatistics(NetPeer peer)
{
m_peer = peer;
Reset();
}
internal void Reset()
{
m_sentPackets = 0;
m_receivedPackets = 0;
m_sentMessages = 0;
m_receivedMessages = 0;
m_sentBytes = 0;
m_receivedBytes = 0;
m_bytesAllocated = 0;
}
/// <summary>
/// Gets the number of sent packets since the NetPeer was initialized
/// </summary>
public int SentPackets { get { return m_sentPackets; } }
/// <summary>
/// Gets the number of received packets since the NetPeer was initialized
/// </summary>
public int ReceivedPackets { get { return m_receivedPackets; } }
/// <summary>
/// Gets the number of sent messages since the NetPeer was initialized
/// </summary>
public int SentMessages { get { return m_sentMessages; } }
/// <summary>
/// Gets the number of received messages since the NetPeer was initialized
/// </summary>
public int ReceivedMessages { get { return m_receivedMessages; } }
/// <summary>
/// Gets the number of sent bytes since the NetPeer was initialized
/// </summary>
public int SentBytes { get { return m_sentBytes; } }
/// <summary>
/// Gets the number of received bytes since the NetPeer was initialized
/// </summary>
public int ReceivedBytes { get { return m_receivedBytes; } }
/// <summary>
/// Gets the number of bytes allocated (and possibly garbage collected) for message storage
/// </summary>
public long StorageBytesAllocated { get { return m_bytesAllocated; } }
/// <summary>
/// Gets the number of bytes in the recycled pool
/// </summary>
public int BytesInRecyclePool { get { return m_peer.m_storagePoolBytes; } }
#if USE_RELEASE_STATISTICS
internal void PacketSent(int numBytes, int numMessages)
{
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#else
[Conditional("DEBUG")]
internal void PacketSent(int numBytes, int numMessages)
{
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS
internal void PacketReceived(int numBytes, int numMessages)
{
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#else
[Conditional("DEBUG")]
internal void PacketReceived(int numBytes, int numMessages)
{
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#endif
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
StringBuilder bdr = new StringBuilder();
bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections");
#if DEBUG || USE_RELEASE_STATISTICS
bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets");
bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages in " + m_receivedPackets + " packets");
#else
bdr.AppendLine("Sent (n/a) bytes in (n/a) messages in (n/a) packets");
bdr.AppendLine("Received (n/a) bytes in (n/a) messages in (n/a) packets");
#endif
bdr.AppendLine("Storage allocated " + m_bytesAllocated + " bytes");
bdr.AppendLine("Recycled pool " + m_peer.m_storagePoolBytes + " bytes");
return bdr.ToString();
}
}
}

View File

@@ -1,49 +1,49 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace Lidgren.Network
{
/// <summary>
/// Status for a NetPeer instance
/// </summary>
public enum NetPeerStatus
{
/// <summary>
/// NetPeer is not running; socket is not bound
/// </summary>
NotRunning = 0,
/// <summary>
/// NetPeer is in the process of starting up
/// </summary>
Starting = 1,
/// <summary>
/// NetPeer is bound to socket and listening for packets
/// </summary>
Running = 2,
/// <summary>
/// Shutdown has been requested and will be executed shortly
/// </summary>
ShutdownRequested = 3,
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
namespace Lidgren.Network
{
/// <summary>
/// Status for a NetPeer instance
/// </summary>
public enum NetPeerStatus
{
/// <summary>
/// NetPeer is not running; socket is not bound
/// </summary>
NotRunning = 0,
/// <summary>
/// NetPeer is in the process of starting up
/// </summary>
Starting = 1,
/// <summary>
/// NetPeer is bound to socket and listening for packets
/// </summary>
Running = 2,
/// <summary>
/// Shutdown has been requested and will be executed shortly
/// </summary>
ShutdownRequested = 3,
}
}

View File

@@ -1,283 +1,283 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Collections.Generic;
// @TODO: examine performance characteristics of using SpinLock when using .Net 4.0
namespace Lidgren.Network
{
/// <summary>
/// Thread safe (blocking) expanding queue with TryDequeue() and EnqueueFirst()
/// </summary>
[DebuggerDisplay("Count={Count} Capacity={Capacity}")]
public sealed class NetQueue<T>
{
// Example:
// m_capacity = 8
// m_size = 6
// m_head = 4
//
// [0] item
// [1] item (tail = ((head + size - 1) % capacity)
// [2]
// [3]
// [4] item (head)
// [5] item
// [6] item
// [7] item
//
private T[] m_items;
private readonly object m_lock;
private int m_size;
private int m_head;
/// <summary>
/// Gets the number of items in the queue
/// </summary>
public int Count { get { return m_size; } }
/// <summary>
/// Gets the current capacity for the queue
/// </summary>
public int Capacity { get { return m_items.Length; } }
/// <summary>
/// NetQueue constructor
/// </summary>
public NetQueue(int initialCapacity)
{
m_lock = new object();
m_items = new T[initialCapacity];
}
/// <summary>
/// Adds an item last/tail of the queue
/// </summary>
public void Enqueue(T item)
{
lock (m_lock)
{
if (m_size == m_items.Length)
SetCapacity(m_items.Length + 8);
int slot = (m_head + m_size) % m_items.Length;
m_items[slot] = item;
m_size++;
}
}
/// <summary>
/// Adds an item last/tail of the queue
/// </summary>
public void Enqueue(IEnumerable<T> items)
{
lock (m_lock)
{
foreach (var item in items)
{
if (m_size == m_items.Length)
SetCapacity(m_items.Length + 8); // @TODO move this out of loop
int slot = (m_head + m_size) % m_items.Length;
m_items[slot] = item;
m_size++;
}
}
}
/// <summary>
/// Places an item first, at the head of the queue
/// </summary>
public void EnqueueFirst(T item)
{
lock (m_lock)
{
if (m_size >= m_items.Length)
SetCapacity(m_items.Length + 8);
m_head--;
if (m_head < 0)
m_head = m_items.Length - 1;
m_items[m_head] = item;
m_size++;
}
}
// must be called from within a lock(m_lock) !
private void SetCapacity(int newCapacity)
{
if (m_size == 0)
{
if (m_size == 0)
{
m_items = new T[newCapacity];
m_head = 0;
return;
}
}
T[] newItems = new T[newCapacity];
if (m_head + m_size - 1 < m_items.Length)
{
Array.Copy(m_items, m_head, newItems, 0, m_size);
}
else
{
Array.Copy(m_items, m_head, newItems, 0, m_items.Length - m_head);
Array.Copy(m_items, 0, newItems, m_items.Length - m_head, (m_size - (m_items.Length - m_head)));
}
m_items = newItems;
m_head = 0;
}
/// <summary>
/// Gets an item from the head of the queue, or returns default(T) if empty
/// </summary>
public bool TryDequeue(out T item)
{
if (m_size == 0)
{
item = default(T);
return false;
}
lock (m_lock)
{
if (m_size == 0)
{
item = default(T);
return false;
}
item = m_items[m_head];
m_items[m_head] = default(T);
m_head = (m_head + 1) % m_items.Length;
m_size--;
return true;
}
}
/// <summary>
/// Gets an item from the head of the queue, or returns default(T) if empty
/// </summary>
public int TryDrain(IList<T> addTo)
{
if (m_size == 0)
return 0;
lock (m_lock)
{
int added = m_size;
while (m_size > 0)
{
var item = m_items[m_head];
addTo.Add(item);
m_items[m_head] = default(T);
m_head = (m_head + 1) % m_items.Length;
m_size--;
}
return added;
}
}
/// <summary>
/// Returns default(T) if queue is empty
/// </summary>
public T TryPeek(int offset)
{
if (m_size == 0)
return default(T);
lock (m_lock)
{
if (m_size == 0)
return default(T);
return m_items[(m_head + offset) % m_items.Length];
}
}
/// <summary>
/// Determines whether an item is in the queue
/// </summary>
public bool Contains(T item)
{
lock (m_lock)
{
int ptr = m_head;
for (int i = 0; i < m_size; i++)
{
if (m_items[ptr] == null)
{
if (item == null)
return true;
}
else
{
if (m_items[ptr].Equals(item))
return true;
}
ptr = (ptr + 1) % m_items.Length;
}
}
return false;
}
/// <summary>
/// Copies the queue items to a new array
/// </summary>
public T[] ToArray()
{
lock (m_lock)
{
T[] retval = new T[m_size];
int ptr = m_head;
for (int i = 0; i < m_size; i++)
{
retval[i] = m_items[ptr++];
if (ptr >= m_items.Length)
ptr = 0;
}
return retval;
}
}
/// <summary>
/// Removes all objects from the queue
/// </summary>
public void Clear()
{
lock (m_lock)
{
for (int i = 0; i < m_items.Length; i++)
m_items[i] = default(T);
m_head = 0;
m_size = 0;
}
}
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Collections.Generic;
// @TODO: examine performance characteristics of using SpinLock when using .Net 4.0
namespace Lidgren.Network
{
/// <summary>
/// Thread safe (blocking) expanding queue with TryDequeue() and EnqueueFirst()
/// </summary>
[DebuggerDisplay("Count={Count} Capacity={Capacity}")]
public sealed class NetQueue<T>
{
// Example:
// m_capacity = 8
// m_size = 6
// m_head = 4
//
// [0] item
// [1] item (tail = ((head + size - 1) % capacity)
// [2]
// [3]
// [4] item (head)
// [5] item
// [6] item
// [7] item
//
private T[] m_items;
private readonly object m_lock;
private int m_size;
private int m_head;
/// <summary>
/// Gets the number of items in the queue
/// </summary>
public int Count { get { return m_size; } }
/// <summary>
/// Gets the current capacity for the queue
/// </summary>
public int Capacity { get { return m_items.Length; } }
/// <summary>
/// NetQueue constructor
/// </summary>
public NetQueue(int initialCapacity)
{
m_lock = new object();
m_items = new T[initialCapacity];
}
/// <summary>
/// Adds an item last/tail of the queue
/// </summary>
public void Enqueue(T item)
{
lock (m_lock)
{
if (m_size == m_items.Length)
SetCapacity(m_items.Length + 8);
int slot = (m_head + m_size) % m_items.Length;
m_items[slot] = item;
m_size++;
}
}
/// <summary>
/// Adds an item last/tail of the queue
/// </summary>
public void Enqueue(IEnumerable<T> items)
{
lock (m_lock)
{
foreach (var item in items)
{
if (m_size == m_items.Length)
SetCapacity(m_items.Length + 8); // @TODO move this out of loop
int slot = (m_head + m_size) % m_items.Length;
m_items[slot] = item;
m_size++;
}
}
}
/// <summary>
/// Places an item first, at the head of the queue
/// </summary>
public void EnqueueFirst(T item)
{
lock (m_lock)
{
if (m_size >= m_items.Length)
SetCapacity(m_items.Length + 8);
m_head--;
if (m_head < 0)
m_head = m_items.Length - 1;
m_items[m_head] = item;
m_size++;
}
}
// must be called from within a lock(m_lock) !
private void SetCapacity(int newCapacity)
{
if (m_size == 0)
{
if (m_size == 0)
{
m_items = new T[newCapacity];
m_head = 0;
return;
}
}
T[] newItems = new T[newCapacity];
if (m_head + m_size - 1 < m_items.Length)
{
Array.Copy(m_items, m_head, newItems, 0, m_size);
}
else
{
Array.Copy(m_items, m_head, newItems, 0, m_items.Length - m_head);
Array.Copy(m_items, 0, newItems, m_items.Length - m_head, (m_size - (m_items.Length - m_head)));
}
m_items = newItems;
m_head = 0;
}
/// <summary>
/// Gets an item from the head of the queue, or returns default(T) if empty
/// </summary>
public bool TryDequeue(out T item)
{
if (m_size == 0)
{
item = default(T);
return false;
}
lock (m_lock)
{
if (m_size == 0)
{
item = default(T);
return false;
}
item = m_items[m_head];
m_items[m_head] = default(T);
m_head = (m_head + 1) % m_items.Length;
m_size--;
return true;
}
}
/// <summary>
/// Gets an item from the head of the queue, or returns default(T) if empty
/// </summary>
public int TryDrain(IList<T> addTo)
{
if (m_size == 0)
return 0;
lock (m_lock)
{
int added = m_size;
while (m_size > 0)
{
var item = m_items[m_head];
addTo.Add(item);
m_items[m_head] = default(T);
m_head = (m_head + 1) % m_items.Length;
m_size--;
}
return added;
}
}
/// <summary>
/// Returns default(T) if queue is empty
/// </summary>
public T TryPeek(int offset)
{
if (m_size == 0)
return default(T);
lock (m_lock)
{
if (m_size == 0)
return default(T);
return m_items[(m_head + offset) % m_items.Length];
}
}
/// <summary>
/// Determines whether an item is in the queue
/// </summary>
public bool Contains(T item)
{
lock (m_lock)
{
int ptr = m_head;
for (int i = 0; i < m_size; i++)
{
if (m_items[ptr] == null)
{
if (item == null)
return true;
}
else
{
if (m_items[ptr].Equals(item))
return true;
}
ptr = (ptr + 1) % m_items.Length;
}
}
return false;
}
/// <summary>
/// Copies the queue items to a new array
/// </summary>
public T[] ToArray()
{
lock (m_lock)
{
T[] retval = new T[m_size];
int ptr = m_head;
for (int i = 0; i < m_size; i++)
{
retval[i] = m_items[ptr++];
if (ptr >= m_items.Length)
ptr = 0;
}
return retval;
}
}
/// <summary>
/// Removes all objects from the queue
/// </summary>
public void Clear()
{
lock (m_lock)
{
for (int i = 0; i < m_items.Length; i++)
m_items[i] = default(T);
m_head = 0;
m_size = 0;
}
}
}
}

View File

@@ -0,0 +1,281 @@
using System;
using System.Security.Cryptography;
namespace Lidgren.Network
{
/// <summary>
/// Multiply With Carry random
/// </summary>
public class MWCRandom : NetRandom
{
/// <summary>
/// Get global instance of MWCRandom
/// </summary>
public static new readonly MWCRandom Instance = new MWCRandom();
private uint m_w, m_z;
/// <summary>
/// Constructor with randomized seed
/// </summary>
public MWCRandom()
{
Initialize(NetRandomSeed.GetUInt64());
}
/// <summary>
/// (Re)initialize this instance with provided 32 bit seed
/// </summary>
[CLSCompliant(false)]
public override void Initialize(uint seed)
{
m_w = seed;
m_z = seed * 16777619;
}
/// <summary>
/// (Re)initialize this instance with provided 64 bit seed
/// </summary>
[CLSCompliant(false)]
public void Initialize(ulong seed)
{
m_w = (uint)seed;
m_z = (uint)(seed >> 32);
}
/// <summary>
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
[CLSCompliant(false)]
public override uint NextUInt32()
{
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
return ((m_z << 16) + m_w);
}
}
/// <summary>
/// Xor Shift based random
/// </summary>
public sealed class XorShiftRandom : NetRandom
{
/// <summary>
/// Get global instance of XorShiftRandom
/// </summary>
public static new readonly XorShiftRandom Instance = new XorShiftRandom();
private const uint c_x = 123456789;
private const uint c_y = 362436069;
private const uint c_z = 521288629;
private const uint c_w = 88675123;
private uint m_x, m_y, m_z, m_w;
/// <summary>
/// Constructor with randomized seed
/// </summary>
public XorShiftRandom()
{
Initialize(NetRandomSeed.GetUInt64());
}
/// <summary>
/// Constructor with provided 64 bit seed
/// </summary>
[CLSCompliant(false)]
public XorShiftRandom(ulong seed)
{
Initialize(seed);
}
/// <summary>
/// (Re)initialize this instance with provided 32 bit seed
/// </summary>
[CLSCompliant(false)]
public override void Initialize(uint seed)
{
m_x = (uint)seed;
m_y = c_y;
m_z = c_z;
m_w = c_w;
}
/// <summary>
/// (Re)initialize this instance with provided 64 bit seed
/// </summary>
[CLSCompliant(false)]
public void Initialize(ulong seed)
{
m_x = (uint)seed;
m_y = c_y;
m_z = (uint)(seed << 32);
m_w = c_w;
}
/// <summary>
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
[CLSCompliant(false)]
public override uint NextUInt32()
{
uint t = (m_x ^ (m_x << 11));
m_x = m_y; m_y = m_z; m_z = m_w;
return (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)));
}
}
/// <summary>
/// Mersenne Twister based random
/// </summary>
public sealed class MersenneTwisterRandom : NetRandom
{
/// <summary>
/// Get global instance of MersenneTwisterRandom
/// </summary>
public static new readonly MersenneTwisterRandom Instance = new MersenneTwisterRandom();
private const int N = 624;
private const int M = 397;
private const uint MATRIX_A = 0x9908b0dfU;
private const uint UPPER_MASK = 0x80000000U;
private const uint LOWER_MASK = 0x7fffffffU;
private const uint TEMPER1 = 0x9d2c5680U;
private const uint TEMPER2 = 0xefc60000U;
private const int TEMPER3 = 11;
private const int TEMPER4 = 7;
private const int TEMPER5 = 15;
private const int TEMPER6 = 18;
private UInt32[] mt;
private int mti;
private UInt32[] mag01;
private const double c_realUnitInt = 1.0 / ((double)int.MaxValue + 1.0);
/// <summary>
/// Constructor with randomized seed
/// </summary>
public MersenneTwisterRandom()
{
Initialize(NetRandomSeed.GetUInt32());
}
/// <summary>
/// Constructor with provided 32 bit seed
/// </summary>
[CLSCompliant(false)]
public MersenneTwisterRandom(uint seed)
{
Initialize(seed);
}
/// <summary>
/// (Re)initialize this instance with provided 32 bit seed
/// </summary>
[CLSCompliant(false)]
public override void Initialize(uint seed)
{
mt = new UInt32[N];
mti = N + 1;
mag01 = new UInt32[] { 0x0U, MATRIX_A };
mt[0] = seed;
for (int i = 1; i < N; i++)
mt[i] = (UInt32)(1812433253 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i);
}
/// <summary>
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
[CLSCompliant(false)]
public override uint NextUInt32()
{
UInt32 y;
if (mti >= N)
{
GenRandAll();
mti = 0;
}
y = mt[mti++];
y ^= (y >> TEMPER3);
y ^= (y << TEMPER4) & TEMPER1;
y ^= (y << TEMPER5) & TEMPER2;
y ^= (y >> TEMPER6);
return y;
}
private void GenRandAll()
{
int kk = 1;
UInt32 y;
UInt32 p;
y = mt[0] & UPPER_MASK;
do
{
p = mt[kk];
mt[kk - 1] = mt[kk + (M - 1)] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
y = p & UPPER_MASK;
} while (++kk < N - M + 1);
do
{
p = mt[kk];
mt[kk - 1] = mt[kk + (M - N - 1)] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
y = p & UPPER_MASK;
} while (++kk < N);
p = mt[0];
mt[N - 1] = mt[M - 1] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
}
}
/// <summary>
/// RNGCryptoServiceProvider based random; very slow but cryptographically safe
/// </summary>
public class CryptoRandom : NetRandom
{
/// <summary>
/// Global instance of CryptoRandom
/// </summary>
public static new readonly CryptoRandom Instance = new CryptoRandom();
private RandomNumberGenerator m_rnd = new RNGCryptoServiceProvider();
/// <summary>
/// Seed in CryptoRandom does not create deterministic sequences
/// </summary>
[CLSCompliant(false)]
public override void Initialize(uint seed)
{
byte[] tmp = new byte[seed % 16];
m_rnd.GetBytes(tmp); // just prime it
}
/// <summary>
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
[CLSCompliant(false)]
public override uint NextUInt32()
{
var bytes = new byte[4];
m_rnd.GetBytes(bytes);
return (uint)bytes[0] | (((uint)bytes[1]) << 8) | (((uint)bytes[2]) << 16) | (((uint)bytes[3]) << 24);
}
/// <summary>
/// Fill the specified buffer with random values
/// </summary>
public override void NextBytes(byte[] buffer)
{
m_rnd.GetBytes(buffer);
}
/// <summary>
/// Fills all bytes from offset to offset + length in buffer with random values
/// </summary>
public override void NextBytes(byte[] buffer, int offset, int length)
{
var bytes = new byte[length];
m_rnd.GetBytes(bytes);
Array.Copy(bytes, 0, buffer, offset, length);
}
}
}

View File

@@ -1,373 +1,333 @@
using System;
namespace Lidgren.Network
{
/// <summary>
/// A fast random number generator for .NET
/// Colin Green, January 2005
/// </summary>
/// September 4th 2005
/// Added NextBytesUnsafe() - commented out by default.
/// Fixed bug in Reinitialise() - y,z and w variables were not being reset.
///
/// Key points:
/// 1) Based on a simple and fast xor-shift pseudo random number generator (RNG) specified in:
/// Marsaglia, George. (2003). Xorshift RNGs.
/// http://www.jstatsoft.org/v08/i14/xorshift.pdf
///
/// This particular implementation of xorshift has a period of 2^128-1. See the above paper to see
/// how this can be easily extened if you need a longer period. At the time of writing I could find no
/// information on the period of System.Random for comparison.
///
/// 2) Faster than System.Random. Up to 8x faster, depending on which methods are called.
///
/// 3) Direct replacement for System.Random. This class implements all of the methods that System.Random
/// does plus some additional methods. The like named methods are functionally equivalent.
///
/// 4) Allows fast re-initialisation with a seed, unlike System.Random which accepts a seed at construction
/// time which then executes a relatively expensive initialisation routine. This provides a vast speed improvement
/// if you need to reset the pseudo-random number sequence many times, e.g. if you want to re-generate the same
/// sequence many times. An alternative might be to cache random numbers in an array, but that approach is limited
/// by memory capacity and the fact that you may also want a large number of different sequences cached. Each sequence
/// can each be represented by a single seed value (int) when using FastRandom.
///
/// Notes.
/// A further performance improvement can be obtained by declaring local variables as static, thus avoiding
/// re-allocation of variables on each call. However care should be taken if multiple instances of
/// FastRandom are in use or if being used in a multi-threaded environment.
public class NetRandom
{
/// <summary>
/// Gets a global NetRandom instance
/// </summary>
public static readonly NetRandom Instance = new NetRandom();
// The +1 ensures NextDouble doesn't generate 1.0
const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0);
const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0);
const uint Y = 842502087, Z = 3579807591, W = 273326509;
private static int s_extraSeed = 42;
uint x, y, z, w;
#region Constructors
/// <summary>
/// Initialises a new instance using time dependent seed.
/// </summary>
public NetRandom()
{
// Initialise using the system tick count.
Reinitialise(GetSeed(this));
}
/// <summary>
/// Initialises a new instance using an int value as seed.
/// This constructor signature is provided to maintain compatibility with
/// System.Random
/// </summary>
public NetRandom(int seed)
{
Reinitialise(seed);
}
/// <summary>
/// Create a semi-random seed based on an object
/// </summary>
public int GetSeed(object forObject)
{
// mix some semi-random properties
int seed = (int)Environment.TickCount;
seed ^= forObject.GetHashCode();
//seed ^= (int)(Stopwatch.GetTimestamp());
//seed ^= (int)(Environment.WorkingSet); // will return 0 on mono
int extraSeed = System.Threading.Interlocked.Increment(ref s_extraSeed);
return seed + extraSeed;
}
#endregion
#region Public Methods [Reinitialisation]
/// <summary>
/// Reinitialises using an int value as a seed.
/// </summary>
/// <param name="seed"></param>
public void Reinitialise(int seed)
{
// The only stipulation stated for the xorshift RNG is that at least one of
// the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing
// resetting of the x seed
x = (uint)seed;
y = Y;
z = Z;
w = W;
}
#endregion
#region Public Methods [System.Random functionally equivalent methods]
/// <summary>
/// Generates a random int over the range 0 to int.MaxValue-1.
/// MaxValue is not generated in order to remain functionally equivalent to System.Random.Next().
/// This does slightly eat into some of the performance gain over System.Random, but not much.
/// For better performance see:
///
/// Call NextInt() for an int over the range 0 to int.MaxValue.
///
/// Call NextUInt() and cast the result to an int to generate an int over the full Int32 value range
/// including negative values.
/// </summary>
/// <returns></returns>
public int Next()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
// Handle the special case where the value int.MaxValue is generated. This is outside of
// the range of permitted values, so we therefore call Next() to try again.
uint rtn = w & 0x7FFFFFFF;
if (rtn == 0x7FFFFFFF)
return Next();
return (int)rtn;
}
/// <summary>
/// Generates a random int over the range 0 to upperBound-1, and not including upperBound.
/// </summary>
/// <param name="upperBound"></param>
/// <returns></returns>
public int Next(int upperBound)
{
if (upperBound < 0)
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
// The explicit int cast before the first multiplication gives better performance.
// See comments in NextDouble.
return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
}
/// <summary>
/// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound.
/// upperBound must be >= lowerBound. lowerBound may be negative.
/// </summary>
/// <param name="lowerBound"></param>
/// <param name="upperBound"></param>
/// <returns></returns>
public int Next(int lowerBound, int upperBound)
{
if (lowerBound > upperBound)
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
// The explicit int cast before the first multiplication gives better performance.
// See comments in NextDouble.
int range = upperBound - lowerBound;
if (range < 0)
{ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
// We also must use all 32 bits of precision, instead of the normal 31, which again is slower.
return lowerBound + (int)((REAL_UNIT_UINT * (double)(w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound));
}
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain
// a little more performance.
return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
}
/// <summary>
/// Generates a random double. Values returned are from 0.0 up to but not including 1.0.
/// </summary>
/// <returns></returns>
public double NextDouble()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
// Here we can gain a 2x speed improvement by generating a value that can be cast to
// an int instead of the more easily available uint. If we then explicitly cast to an
// int the compiler will then cast the int to a double to perform the multiplication,
// this final cast is a lot faster than casting from a uint to a double. The extra cast
// to an int is very fast (the allocated bits remain the same) and so the overall effect
// of the extra cast is a significant performance improvement.
//
// Also note that the loss of one bit of precision is equivalent to what occurs within
// System.Random.
return (REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))));
}
/// <summary>
/// Generates a random single. Values returned are from 0.0 up to but not including 1.0.
/// </summary>
public float NextSingle()
{
return (float)NextDouble();
}
/// <summary>
/// Fills the provided byte array with random bytes.
/// This method is functionally equivalent to System.Random.NextBytes().
/// </summary>
/// <param name="buffer"></param>
public void NextBytes(byte[] buffer)
{
// Fill up the bulk of the buffer in chunks of 4 bytes at a time.
uint x = this.x, y = this.y, z = this.z, w = this.w;
int i = 0;
uint t;
for (int bound = buffer.Length - 3; i < bound; )
{
// Generate 4 bytes.
// Increased performance is achieved by generating 4 random bytes per loop.
// Also note that no mask needs to be applied to zero out the higher order bytes before
// casting because the cast ignores thos bytes. Thanks to Stefan Troschütz for pointing this out.
t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
buffer[i++] = (byte)w;
buffer[i++] = (byte)(w >> 8);
buffer[i++] = (byte)(w >> 16);
buffer[i++] = (byte)(w >> 24);
}
// Fill up any remaining bytes in the buffer.
if (i < buffer.Length)
{
// Generate 4 bytes.
t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
buffer[i++] = (byte)w;
if (i < buffer.Length)
{
buffer[i++] = (byte)(w >> 8);
if (i < buffer.Length)
{
buffer[i++] = (byte)(w >> 16);
if (i < buffer.Length)
{
buffer[i] = (byte)(w >> 24);
}
}
}
}
this.x = x; this.y = y; this.z = z; this.w = w;
}
// /// <summary>
// /// A version of NextBytes that uses a pointer to set 4 bytes of the byte buffer in one operation
// /// thus providing a nice speedup. The loop is also partially unrolled to allow out-of-order-execution,
// /// this results in about a x2 speedup on an AMD Athlon. Thus performance may vary wildly on different CPUs
// /// depending on the number of execution units available.
// ///
// /// Another significant speedup is obtained by setting the 4 bytes by indexing pDWord (e.g. pDWord[i++]=w)
// /// instead of adjusting it dereferencing it (e.g. *pDWord++=w).
// ///
// /// Note that this routine requires the unsafe compilation flag to be specified and so is commented out by default.
// /// </summary>
// /// <param name="buffer"></param>
// public unsafe void NextBytesUnsafe(byte[] buffer)
// {
// if(buffer.Length % 8 != 0)
// throw new ArgumentException("Buffer length must be divisible by 8", "buffer");
//
// uint x=this.x, y=this.y, z=this.z, w=this.w;
//
// fixed(byte* pByte0 = buffer)
// {
// uint* pDWord = (uint*)pByte0;
// for(int i=0, len=buffer.Length>>2; i < len; i+=2)
// {
// uint t=(x^(x<<11));
// x=y; y=z; z=w;
// pDWord[i] = w = (w^(w>>19))^(t^(t>>8));
//
// t=(x^(x<<11));
// x=y; y=z; z=w;
// pDWord[i+1] = w = (w^(w>>19))^(t^(t>>8));
// }
// }
//
// this.x=x; this.y=y; this.z=z; this.w=w;
// }
#endregion
#region Public Methods [Methods not present on System.Random]
/// <summary>
/// Generates a uint. Values returned are over the full range of a uint,
/// uint.MinValue to uint.MaxValue, inclusive.
///
/// This is the fastest method for generating a single random number because the underlying
/// random number generator algorithm generates 32 random bits that can be cast directly to
/// a uint.
/// </summary>
[CLSCompliant(false)]
public uint NextUInt()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
}
/// <summary>
/// Generates a random int over the range 0 to int.MaxValue, inclusive.
/// This method differs from Next() only in that the range is 0 to int.MaxValue
/// and not 0 to int.MaxValue-1.
///
/// The slight difference in range means this method is slightly faster than Next()
/// but is not functionally equivalent to System.Random.Next().
/// </summary>
/// <returns></returns>
public int NextInt()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
return (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))));
}
// Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned
// with bitBufferIdx.
uint bitBuffer;
uint bitMask = 1;
/// <summary>
/// Generates a single random bit.
/// This method's performance is improved by generating 32 bits in one operation and storing them
/// ready for future calls.
/// </summary>
/// <returns></returns>
public bool NextBool()
{
if (bitMask == 1)
{
// Generate 32 more bits.
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
bitBuffer = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
// Reset the bitMask that tells us which bit to read next.
bitMask = 0x80000000;
return (bitBuffer & bitMask) == 0;
}
return (bitBuffer & (bitMask >>= 1)) == 0;
}
#endregion
}
}
using System;
namespace Lidgren.Network
{
/// <summary>
/// A fast random number generator for .NET
/// Colin Green, January 2005
/// </summary>
/// September 4th 2005
/// Added NextBytesUnsafe() - commented out by default.
/// Fixed bug in Reinitialise() - y,z and w variables were not being reset.
///
/// Key points:
/// 1) Based on a simple and fast xor-shift pseudo random number generator (RNG) specified in:
/// Marsaglia, George. (2003). Xorshift RNGs.
/// http://www.jstatsoft.org/v08/i14/xorshift.pdf
///
/// This particular implementation of xorshift has a period of 2^128-1. See the above paper to see
/// how this can be easily extened if you need a longer period. At the time of writing I could find no
/// information on the period of System.Random for comparison.
///
/// 2) Faster than System.Random. Up to 8x faster, depending on which methods are called.
///
/// 3) Direct replacement for System.Random. This class implements all of the methods that System.Random
/// does plus some additional methods. The like named methods are functionally equivalent.
///
/// 4) Allows fast re-initialisation with a seed, unlike System.Random which accepts a seed at construction
/// time which then executes a relatively expensive initialisation routine. This provides a vast speed improvement
/// if you need to reset the pseudo-random number sequence many times, e.g. if you want to re-generate the same
/// sequence many times. An alternative might be to cache random numbers in an array, but that approach is limited
/// by memory capacity and the fact that you may also want a large number of different sequences cached. Each sequence
/// can each be represented by a single seed value (int) when using FastRandom.
///
/// Notes.
/// A further performance improvement can be obtained by declaring local variables as static, thus avoiding
/// re-allocation of variables on each call. However care should be taken if multiple instances of
/// FastRandom are in use or if being used in a multi-threaded environment.
public class NetRandom
{
/// <summary>
/// Gets a global NetRandom instance
/// </summary>
public static readonly NetRandom Instance = new NetRandom();
// The +1 ensures NextDouble doesn't generate 1.0
const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0);
const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0);
const uint Y = 842502087, Z = 3579807591, W = 273326509;
private static int s_extraSeed = 42;
uint x, y, z, w;
#region Constructors
/// <summary>
/// Initialises a new instance using time dependent seed.
/// </summary>
public NetRandom()
{
// Initialise using the system tick count.
Reinitialise(GetSeed(this));
}
/// <summary>
/// Initialises a new instance using an int value as seed.
/// This constructor signature is provided to maintain compatibility with
/// System.Random
/// </summary>
public NetRandom(int seed)
{
Reinitialise(seed);
}
/// <summary>
/// Create a semi-random seed based on an object
/// </summary>
public int GetSeed(object forObject)
{
// mix some semi-random properties
int seed = (int)Environment.TickCount;
seed ^= forObject.GetHashCode();
int extraSeed = System.Threading.Interlocked.Increment(ref s_extraSeed);
return seed + extraSeed;
}
#endregion
#region Public Methods [Reinitialisation]
/// <summary>
/// Reinitialises using an int value as a seed.
/// </summary>
/// <param name="seed"></param>
public void Reinitialise(int seed)
{
// The only stipulation stated for the xorshift RNG is that at least one of
// the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing
// resetting of the x seed
x = (uint)seed;
y = Y;
z = Z;
w = W;
}
#endregion
#region Public Methods [System.Random functionally equivalent methods]
/// <summary>
/// Generates a random int over the range 0 to int.MaxValue-1.
/// MaxValue is not generated in order to remain functionally equivalent to System.Random.Next().
/// This does slightly eat into some of the performance gain over System.Random, but not much.
/// For better performance see:
///
/// Call NextInt() for an int over the range 0 to int.MaxValue.
///
/// Call NextUInt() and cast the result to an int to generate an int over the full Int32 value range
/// including negative values.
/// </summary>
/// <returns></returns>
public int Next()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
// Handle the special case where the value int.MaxValue is generated. This is outside of
// the range of permitted values, so we therefore call Next() to try again.
uint rtn = w & 0x7FFFFFFF;
if (rtn == 0x7FFFFFFF)
return Next();
return (int)rtn;
}
/// <summary>
/// Generates a random int over the range 0 to upperBound-1, and not including upperBound.
/// </summary>
/// <param name="upperBound"></param>
/// <returns></returns>
public int Next(int upperBound)
{
if (upperBound < 0)
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
// The explicit int cast before the first multiplication gives better performance.
// See comments in NextDouble.
return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
}
/// <summary>
/// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound.
/// upperBound must be >= lowerBound. lowerBound may be negative.
/// </summary>
/// <param name="lowerBound"></param>
/// <param name="upperBound"></param>
/// <returns></returns>
public int Next(int lowerBound, int upperBound)
{
if (lowerBound > upperBound)
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
// The explicit int cast before the first multiplication gives better performance.
// See comments in NextDouble.
int range = upperBound - lowerBound;
if (range < 0)
{ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
// We also must use all 32 bits of precision, instead of the normal 31, which again is slower.
return lowerBound + (int)((REAL_UNIT_UINT * (double)(w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound));
}
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain
// a little more performance.
return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
}
/// <summary>
/// Generates a random double. Values returned are from 0.0 up to but not including 1.0.
/// </summary>
/// <returns></returns>
public double NextDouble()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
// Here we can gain a 2x speed improvement by generating a value that can be cast to
// an int instead of the more easily available uint. If we then explicitly cast to an
// int the compiler will then cast the int to a double to perform the multiplication,
// this final cast is a lot faster than casting from a uint to a double. The extra cast
// to an int is very fast (the allocated bits remain the same) and so the overall effect
// of the extra cast is a significant performance improvement.
//
// Also note that the loss of one bit of precision is equivalent to what occurs within
// System.Random.
return (REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))));
}
/// <summary>
/// Generates a random single. Values returned are from 0.0 up to but not including 1.0.
/// </summary>
public float NextSingle()
{
return (float)NextDouble();
}
/// <summary>
/// Fills the provided byte array with random bytes.
/// This method is functionally equivalent to System.Random.NextBytes().
/// </summary>
/// <param name="buffer"></param>
public void NextBytes(byte[] buffer)
{
// Fill up the bulk of the buffer in chunks of 4 bytes at a time.
uint x = this.x, y = this.y, z = this.z, w = this.w;
int i = 0;
uint t;
for (int bound = buffer.Length - 3; i < bound; )
{
// Generate 4 bytes.
// Increased performance is achieved by generating 4 random bytes per loop.
// Also note that no mask needs to be applied to zero out the higher order bytes before
// casting because the cast ignores thos bytes. Thanks to Stefan Troschütz for pointing this out.
t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
buffer[i++] = (byte)w;
buffer[i++] = (byte)(w >> 8);
buffer[i++] = (byte)(w >> 16);
buffer[i++] = (byte)(w >> 24);
}
// Fill up any remaining bytes in the buffer.
if (i < buffer.Length)
{
// Generate 4 bytes.
t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
buffer[i++] = (byte)w;
if (i < buffer.Length)
{
buffer[i++] = (byte)(w >> 8);
if (i < buffer.Length)
{
buffer[i++] = (byte)(w >> 16);
if (i < buffer.Length)
{
buffer[i] = (byte)(w >> 24);
}
}
}
}
this.x = x; this.y = y; this.z = z; this.w = w;
}
#endregion
#region Public Methods [Methods not present on System.Random]
/// <summary>
/// Generates a uint. Values returned are over the full range of a uint,
/// uint.MinValue to uint.MaxValue, inclusive.
///
/// This is the fastest method for generating a single random number because the underlying
/// random number generator algorithm generates 32 random bits that can be cast directly to
/// a uint.
/// </summary>
[CLSCompliant(false)]
public uint NextUInt()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
}
/// <summary>
/// Generates a random int over the range 0 to int.MaxValue, inclusive.
/// This method differs from Next() only in that the range is 0 to int.MaxValue
/// and not 0 to int.MaxValue-1.
///
/// The slight difference in range means this method is slightly faster than Next()
/// but is not functionally equivalent to System.Random.Next().
/// </summary>
/// <returns></returns>
public int NextInt()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
return (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))));
}
// Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned
// with bitBufferIdx.
uint bitBuffer;
uint bitMask = 1;
/// <summary>
/// Generates a single random bit.
/// This method's performance is improved by generating 32 bits in one operation and storing them
/// ready for future calls.
/// </summary>
/// <returns></returns>
public bool NextBool()
{
if (bitMask == 1)
{
// Generate 32 more bits.
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
bitBuffer = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
// Reset the bitMask that tells us which bit to read next.
bitMask = 0x80000000;
return (bitBuffer & bitMask) == 0;
}
return (bitBuffer & (bitMask >>= 1)) == 0;
}
#endregion
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// Class for generating random seeds
/// </summary>
public static class NetRandomSeed
{
private static int m_seedIncrement = -1640531527;
/// <summary>
/// Generates a 32 bit random seed
/// </summary>
[CLSCompliant(false)]
public static uint GetUInt32()
{
ulong seed = GetUInt64();
uint low = (uint)seed;
uint high = (uint)(seed >> 32);
return low ^ high;
}
/// <summary>
/// Generates a 64 bit random seed
/// </summary>
[CLSCompliant(false)]
public static ulong GetUInt64()
{
var guidBytes = Guid.NewGuid().ToByteArray();
ulong seed =
((ulong)guidBytes[0] << (8 * 0)) |
((ulong)guidBytes[1] << (8 * 1)) |
((ulong)guidBytes[2] << (8 * 2)) |
((ulong)guidBytes[3] << (8 * 3)) |
((ulong)guidBytes[4] << (8 * 4)) |
((ulong)guidBytes[5] << (8 * 5)) |
((ulong)guidBytes[6] << (8 * 6)) |
((ulong)guidBytes[7] << (8 * 7));
return seed ^ NetUtility.GetPlatformSeed(m_seedIncrement);
}
}
}

View File

@@ -1,18 +1,18 @@
using System;
namespace Lidgren.Network
{
internal abstract class NetReceiverChannelBase
{
internal NetPeer m_peer;
internal NetConnection m_connection;
public NetReceiverChannelBase(NetConnection connection)
{
m_connection = connection;
m_peer = connection.m_peer;
}
internal abstract void ReceiveMessage(NetIncomingMessage msg);
}
}
using System;
namespace Lidgren.Network
{
internal abstract class NetReceiverChannelBase
{
internal NetPeer m_peer;
internal NetConnection m_connection;
public NetReceiverChannelBase(NetConnection connection)
{
m_connection = connection;
m_peer = connection.m_peer;
}
internal abstract void ReceiveMessage(NetIncomingMessage msg);
}
}

View File

@@ -1,87 +1,80 @@
using System;
namespace Lidgren.Network
{
internal sealed class NetReliableOrderedReceiver : NetReceiverChannelBase
{
private int m_windowStart;
private int m_windowSize;
private NetBitVector m_earlyReceived;
internal NetIncomingMessage[] m_withheldMessages;
public NetReliableOrderedReceiver(NetConnection connection, int windowSize)
: base(connection)
{
m_windowSize = windowSize;
m_withheldMessages = new NetIncomingMessage[windowSize];
m_earlyReceived = new NetBitVector(windowSize);
}
private void AdvanceWindow()
{
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
internal override void ReceiveMessage(NetIncomingMessage message)
{
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
// ack no matter what
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
if (relate == 0)
{
// Log("Received message #" + message.SequenceNumber + " right on time");
//
// excellent, right on time
//
//m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);
AdvanceWindow();
m_peer.ReleaseMessage(message);
// release withheld messages
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
while (m_earlyReceived[nextSeqNr % m_windowSize])
{
message = m_withheldMessages[nextSeqNr % m_windowSize];
NetException.Assert(message != null);
// remove it from withheld messages
m_withheldMessages[nextSeqNr % m_windowSize] = null;
m_peer.LogVerbose("Releasing withheld message #" + message);
m_peer.ReleaseMessage(message);
AdvanceWindow();
nextSeqNr++;
}
return;
}
if (relate < 0)
{
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
// duplicate
return;
}
// relate > 0 = early message
if (relate > m_windowSize)
{
// too early message!
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return;
}
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
}
}
}
using System;
namespace Lidgren.Network
{
internal sealed class NetReliableOrderedReceiver : NetReceiverChannelBase
{
private int m_windowStart;
private int m_windowSize;
private NetBitVector m_earlyReceived;
internal NetIncomingMessage[] m_withheldMessages;
public NetReliableOrderedReceiver(NetConnection connection, int windowSize)
: base(connection)
{
m_windowSize = windowSize;
m_withheldMessages = new NetIncomingMessage[windowSize];
m_earlyReceived = new NetBitVector(windowSize);
}
private void AdvanceWindow()
{
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
internal override void ReceiveMessage(NetIncomingMessage message)
{
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
// ack no matter what
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
if (relate == 0)
{
AdvanceWindow();
m_peer.ReleaseMessage(message);
// release withheld messages
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
while (m_earlyReceived[nextSeqNr % m_windowSize])
{
message = m_withheldMessages[nextSeqNr % m_windowSize];
NetException.Assert(message != null);
// remove it from withheld messages
m_withheldMessages[nextSeqNr % m_windowSize] = null;
m_peer.LogVerbose("Releasing withheld message #" + message);
m_peer.ReleaseMessage(message);
AdvanceWindow();
nextSeqNr++;
}
return;
}
if (relate < 0)
{
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
// duplicate
return;
}
// relate > 0 = early message
if (relate > m_windowSize)
{
// too early message!
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return;
}
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
}
}
}

View File

@@ -1,258 +1,243 @@
using System;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// Sender part of Selective repeat ARQ for a particular NetChannel
/// </summary>
internal sealed class NetReliableSenderChannel : NetSenderChannelBase
{
private NetConnection m_connection;
private int m_windowStart;
private int m_windowSize;
private int m_sendStart;
private NetBitVector m_receivedAcks;
internal NetStoredReliableMessage[] m_storedMessages;
internal float m_resendDelay;
internal override int WindowSize { get { return m_windowSize; } }
internal NetReliableSenderChannel(NetConnection connection, int windowSize)
{
m_connection = connection;
m_windowSize = windowSize;
m_windowStart = 0;
m_sendStart = 0;
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
m_storedMessages = new NetStoredReliableMessage[m_windowSize];
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
m_resendDelay = m_connection.GetResendDelay();
}
internal override int GetAllowedSends()
{
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
NetException.Assert(retval >= 0 && retval <= m_windowSize);
return retval;
}
internal override void Reset()
{
m_receivedAcks.Clear();
for (int i = 0; i < m_storedMessages.Length; i++)
m_storedMessages[i].Reset();
m_queuedSends.Clear();
m_windowStart = 0;
m_sendStart = 0;
}
internal override NetSendResult Enqueue(NetOutgoingMessage message)
{
m_queuedSends.Enqueue(message);
int queueLen = m_queuedSends.Count;
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
if (queueLen <= left)
return NetSendResult.Sent;
return NetSendResult.Queued;
}
// call this regularely
internal override void SendQueuedMessages(float now)
{
//
// resends
//
for (int i = 0; i < m_storedMessages.Length; i++)
{
NetOutgoingMessage om = m_storedMessages[i].Message;
if (om == null)
continue;
float t = m_storedMessages[i].LastSent;
if (t > 0 && (now - t) > m_resendDelay)
{
// deduce sequence number
int startSlot = m_windowStart % m_windowSize;
int seqNr = m_windowStart;
while (startSlot != i)
{
startSlot--;
if (startSlot < 0)
startSlot = m_windowSize - 1;
seqNr--;
}
//m_connection.m_peer.LogVerbose("Resending due to delay #" + seqNr + " " + om.ToString());
m_connection.m_statistics.MessageResent(MessageResendReason.Delay);
m_connection.QueueSendMessage(om, seqNr);
m_storedMessages[i].LastSent = now;
m_storedMessages[i].NumSent++;
}
}
int num = GetAllowedSends();
if (num < 1)
return;
// queued sends
while (m_queuedSends.Count > 0 && num > 0)
{
NetOutgoingMessage om;
if (m_queuedSends.TryDequeue(out om))
ExecuteSend(now, om);
num--;
NetException.Assert(num == GetAllowedSends());
}
}
private void ExecuteSend(float now, NetOutgoingMessage message)
{
int seqNr = m_sendStart;
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
m_connection.QueueSendMessage(message, seqNr);
int storeIndex = seqNr % m_windowSize;
NetException.Assert(m_storedMessages[storeIndex].Message == null);
m_storedMessages[storeIndex].NumSent++;
m_storedMessages[storeIndex].Message = message;
m_storedMessages[storeIndex].LastSent = now;
return;
}
private void DestoreMessage(int storeIndex)
{
NetOutgoingMessage storedMessage = m_storedMessages[storeIndex].Message;
#if DEBUG
if (storedMessage == null)
throw new NetException("m_storedMessages[" + storeIndex + "].Message is null; sent " + m_storedMessages[storeIndex].NumSent + " times, last time " + (NetTime.Now - m_storedMessages[storeIndex].LastSent) + " seconds ago");
#else
if (storedMessage != null)
{
#endif
Interlocked.Decrement(ref storedMessage.m_recyclingCount);
if (storedMessage.m_recyclingCount <= 0)
m_connection.m_peer.Recycle(storedMessage);
#if !DEBUG
}
#endif
m_storedMessages[storeIndex] = new NetStoredReliableMessage();
}
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
// seqNr is the actual nr received
internal override void ReceiveAcknowledge(float now, int seqNr)
{
// late (dupe), on time or early ack?
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
if (relate < 0)
{
//m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
return; // late/duplicate ack
}
if (relate == 0)
{
//m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);
// ack arrived right on time
NetException.Assert(seqNr == m_windowStart);
m_receivedAcks[m_windowStart] = false;
DestoreMessage(m_windowStart % m_windowSize);
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
// advance window if we already have early acks
while (m_receivedAcks.Get(m_windowStart))
{
//m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "...");
m_receivedAcks[m_windowStart] = false;
DestoreMessage(m_windowStart % m_windowSize);
NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
//m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart);
}
return;
}
//
// early ack... (if it has been sent!)
//
// If it has been sent either the m_windowStart message was lost
// ... or the ack for that message was lost
//
//m_connection.m_peer.LogDebug("Received early ack for #" + seqNr);
int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart);
if (sendRelate <= 0)
{
// yes, we've sent this message - it's an early (but valid) ack
if (m_receivedAcks[seqNr])
{
// we've already destored/been acked for this message
}
else
{
m_receivedAcks[seqNr] = true;
}
}
else if (sendRelate > 0)
{
// uh... we haven't sent this message yet? Weird, dupe or error...
NetException.Assert(false, "Got ack for message not yet sent?");
return;
}
// Ok, lets resend all missing acks
int rnr = seqNr;
do
{
rnr--;
if (rnr < 0)
rnr = NetConstants.NumSequenceNumbers - 1;
if (m_receivedAcks[rnr])
{
// m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)");
}
else
{
int slot = rnr % m_windowSize;
NetException.Assert(m_storedMessages[slot].Message != null);
if (m_storedMessages[slot].NumSent == 1)
{
// just sent once; resend immediately since we found gap in ack sequence
NetOutgoingMessage rmsg = m_storedMessages[slot].Message;
//m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")");
if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35f))
{
// already resent recently
}
else
{
m_storedMessages[slot].LastSent = now;
m_storedMessages[slot].NumSent++;
m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence);
m_connection.QueueSendMessage(rmsg, rnr);
}
}
}
} while (rnr != m_windowStart);
}
}
}
using System;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// Sender part of Selective repeat ARQ for a particular NetChannel
/// </summary>
internal sealed class NetReliableSenderChannel : NetSenderChannelBase
{
private NetConnection m_connection;
private int m_windowStart;
private int m_windowSize;
private int m_sendStart;
private NetBitVector m_receivedAcks;
internal NetStoredReliableMessage[] m_storedMessages;
internal float m_resendDelay;
internal override int WindowSize { get { return m_windowSize; } }
internal NetReliableSenderChannel(NetConnection connection, int windowSize)
{
m_connection = connection;
m_windowSize = windowSize;
m_windowStart = 0;
m_sendStart = 0;
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
m_storedMessages = new NetStoredReliableMessage[m_windowSize];
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
m_resendDelay = m_connection.GetResendDelay();
}
internal override int GetAllowedSends()
{
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
NetException.Assert(retval >= 0 && retval <= m_windowSize);
return retval;
}
internal override void Reset()
{
m_receivedAcks.Clear();
for (int i = 0; i < m_storedMessages.Length; i++)
m_storedMessages[i].Reset();
m_queuedSends.Clear();
m_windowStart = 0;
m_sendStart = 0;
}
internal override NetSendResult Enqueue(NetOutgoingMessage message)
{
m_queuedSends.Enqueue(message);
int queueLen = m_queuedSends.Count;
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
if (queueLen <= left)
return NetSendResult.Sent;
return NetSendResult.Queued;
}
// call this regularely
internal override void SendQueuedMessages(float now)
{
//
// resends
//
for (int i = 0; i < m_storedMessages.Length; i++)
{
NetOutgoingMessage om = m_storedMessages[i].Message;
if (om == null)
continue;
float t = m_storedMessages[i].LastSent;
if (t > 0 && (now - t) > m_resendDelay)
{
// deduce sequence number
int startSlot = m_windowStart % m_windowSize;
int seqNr = m_windowStart;
while (startSlot != i)
{
startSlot--;
if (startSlot < 0)
startSlot = m_windowSize - 1;
seqNr--;
}
m_connection.m_statistics.MessageResent(MessageResendReason.Delay);
m_connection.QueueSendMessage(om, seqNr);
m_storedMessages[i].LastSent = now;
m_storedMessages[i].NumSent++;
}
}
int num = GetAllowedSends();
if (num < 1)
return;
// queued sends
while (m_queuedSends.Count > 0 && num > 0)
{
NetOutgoingMessage om;
if (m_queuedSends.TryDequeue(out om))
ExecuteSend(now, om);
num--;
NetException.Assert(num == GetAllowedSends());
}
}
private void ExecuteSend(float now, NetOutgoingMessage message)
{
int seqNr = m_sendStart;
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
m_connection.QueueSendMessage(message, seqNr);
int storeIndex = seqNr % m_windowSize;
NetException.Assert(m_storedMessages[storeIndex].Message == null);
m_storedMessages[storeIndex].NumSent++;
m_storedMessages[storeIndex].Message = message;
m_storedMessages[storeIndex].LastSent = now;
return;
}
private void DestoreMessage(int storeIndex)
{
NetOutgoingMessage storedMessage = m_storedMessages[storeIndex].Message;
#if DEBUG
if (storedMessage == null)
throw new NetException("m_storedMessages[" + storeIndex + "].Message is null; sent " + m_storedMessages[storeIndex].NumSent + " times, last time " + (NetTime.Now - m_storedMessages[storeIndex].LastSent) + " seconds ago");
#else
if (storedMessage != null)
{
#endif
Interlocked.Decrement(ref storedMessage.m_recyclingCount);
if (storedMessage.m_recyclingCount <= 0)
m_connection.m_peer.Recycle(storedMessage);
#if !DEBUG
}
#endif
m_storedMessages[storeIndex] = new NetStoredReliableMessage();
}
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
// seqNr is the actual nr received
internal override void ReceiveAcknowledge(float now, int seqNr)
{
// late (dupe), on time or early ack?
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
if (relate < 0)
return; // late/duplicate ack
if (relate == 0)
{
// ack arrived right on time
NetException.Assert(seqNr == m_windowStart);
m_receivedAcks[m_windowStart] = false;
DestoreMessage(m_windowStart % m_windowSize);
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
// advance window if we already have early acks
while (m_receivedAcks.Get(m_windowStart))
{
m_receivedAcks[m_windowStart] = false;
DestoreMessage(m_windowStart % m_windowSize);
NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
return;
}
//
// early ack... (if it has been sent!)
//
// If it has been sent either the m_windowStart message was lost
// ... or the ack for that message was lost
//
int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart);
if (sendRelate <= 0)
{
// yes, we've sent this message - it's an early (but valid) ack
if (m_receivedAcks[seqNr])
{
// we've already destored/been acked for this message
}
else
{
m_receivedAcks[seqNr] = true;
}
}
else if (sendRelate > 0)
{
// uh... we haven't sent this message yet? Weird, dupe or error...
NetException.Assert(false, "Got ack for message not yet sent?");
return;
}
// Ok, lets resend all missing acks
int rnr = seqNr;
do
{
rnr--;
if (rnr < 0)
rnr = NetConstants.NumSequenceNumbers - 1;
if (!m_receivedAcks[rnr])
{
int slot = rnr % m_windowSize;
NetException.Assert(m_storedMessages[slot].Message != null);
if (m_storedMessages[slot].NumSent == 1)
{
// just sent once; resend immediately since we found gap in ack sequence
NetOutgoingMessage rmsg = m_storedMessages[slot].Message;
if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35f))
{
// already resent recently
}
else
{
m_storedMessages[slot].LastSent = now;
m_storedMessages[slot].NumSent++;
m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence);
m_connection.QueueSendMessage(rmsg, rnr);
}
}
}
} while (rnr != m_windowStart);
}
}
}

View File

@@ -1,63 +1,57 @@
using System;
namespace Lidgren.Network
{
internal sealed class NetReliableSequencedReceiver : NetReceiverChannelBase
{
private int m_windowStart;
private int m_windowSize;
public NetReliableSequencedReceiver(NetConnection connection, int windowSize)
: base(connection)
{
m_windowSize = windowSize;
}
private void AdvanceWindow()
{
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
internal override void ReceiveMessage(NetIncomingMessage message)
{
int nr = message.m_sequenceNumber;
int relate = NetUtility.RelativeSequenceNumber(nr, m_windowStart);
// ack no matter what
m_connection.QueueAck(message.m_receivedMessageType, nr);
if (relate == 0)
{
// Log("Received message #" + message.SequenceNumber + " right on time");
//
// excellent, right on time
//
AdvanceWindow();
m_peer.ReleaseMessage(message);
return;
}
if (relate < 0)
{
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE");
return;
}
// relate > 0 = early message
if (relate > m_windowSize)
{
// too early message!
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return;
}
// ok
m_windowStart = (m_windowStart + relate) % NetConstants.NumSequenceNumbers;
m_peer.ReleaseMessage(message);
return;
}
}
}
using System;
namespace Lidgren.Network
{
internal sealed class NetReliableSequencedReceiver : NetReceiverChannelBase
{
private int m_windowStart;
private int m_windowSize;
public NetReliableSequencedReceiver(NetConnection connection, int windowSize)
: base(connection)
{
m_windowSize = windowSize;
}
private void AdvanceWindow()
{
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
internal override void ReceiveMessage(NetIncomingMessage message)
{
int nr = message.m_sequenceNumber;
int relate = NetUtility.RelativeSequenceNumber(nr, m_windowStart);
// ack no matter what
m_connection.QueueAck(message.m_receivedMessageType, nr);
if (relate == 0)
{
AdvanceWindow();
m_peer.ReleaseMessage(message);
return;
}
if (relate < 0)
{
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE");
return;
}
// relate > 0 = early message
if (relate > m_windowSize)
{
// too early message!
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return;
}
// ok
m_windowStart = (m_windowStart + relate) % NetConstants.NumSequenceNumbers;
m_peer.ReleaseMessage(message);
return;
}
}
}

View File

@@ -1,87 +1,68 @@
using System;
namespace Lidgren.Network
{
internal sealed class NetReliableUnorderedReceiver : NetReceiverChannelBase
{
private int m_windowStart;
private int m_windowSize;
private NetBitVector m_earlyReceived;
public NetReliableUnorderedReceiver(NetConnection connection, int windowSize)
: base(connection)
{
m_windowSize = windowSize;
m_earlyReceived = new NetBitVector(windowSize);
}
private void AdvanceWindow()
{
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
internal override void ReceiveMessage(NetIncomingMessage message)
{
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
// ack no matter what
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
if (relate == 0)
{
// Log("Received message #" + message.SequenceNumber + " right on time");
//
// excellent, right on time
//
//m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);
AdvanceWindow();
m_peer.ReleaseMessage(message);
// release withheld messages
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
while (m_earlyReceived[nextSeqNr % m_windowSize])
{
//message = m_withheldMessages[nextSeqNr % m_windowSize];
//NetException.Assert(message != null);
// remove it from withheld messages
//m_withheldMessages[nextSeqNr % m_windowSize] = null;
//m_peer.LogVerbose("Releasing withheld message #" + message);
//m_peer.ReleaseMessage(message);
AdvanceWindow();
nextSeqNr++;
}
return;
}
if (relate < 0)
{
// duplicate
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
return;
}
// relate > 0 = early message
if (relate > m_windowSize)
{
// too early message!
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return;
}
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
//m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
//m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
m_peer.ReleaseMessage(message);
}
}
}
using System;
namespace Lidgren.Network
{
internal sealed class NetReliableUnorderedReceiver : NetReceiverChannelBase
{
private int m_windowStart;
private int m_windowSize;
private NetBitVector m_earlyReceived;
public NetReliableUnorderedReceiver(NetConnection connection, int windowSize)
: base(connection)
{
m_windowSize = windowSize;
m_earlyReceived = new NetBitVector(windowSize);
}
private void AdvanceWindow()
{
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
internal override void ReceiveMessage(NetIncomingMessage message)
{
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
// ack no matter what
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
if (relate == 0)
{
AdvanceWindow();
m_peer.ReleaseMessage(message);
// release withheld messages
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
while (m_earlyReceived[nextSeqNr % m_windowSize])
{
AdvanceWindow();
nextSeqNr++;
}
return;
}
if (relate < 0)
{
// duplicate
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
return;
}
// relate > 0 = early message
if (relate > m_windowSize)
{
// too early message!
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return;
}
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
m_peer.ReleaseMessage(message);
}
}
}

View File

@@ -1,204 +1,203 @@
#define USE_SHA256
using System;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Helper methods for implementing SRP authentication
/// </summary>
public static class NetSRP
{
private static readonly NetBigInteger N = new NetBigInteger("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16);
private static readonly NetBigInteger g = NetBigInteger.Two;
private static readonly NetBigInteger k = ComputeMultiplier();
private static HashAlgorithm GetHashAlgorithm()
{
#if USE_SHA256
// this does not seem to work as of yet
return SHA256.Create();
#else
return SHA1.Create();
#endif
}
/// <summary>
/// Compute multiplier (k)
/// </summary>
private static NetBigInteger ComputeMultiplier()
{
string one = NetUtility.ToHexString(N.ToByteArrayUnsigned());
string two = NetUtility.ToHexString(g.ToByteArrayUnsigned());
string ccstr = one + two.PadLeft(one.Length, '0');
byte[] cc = NetUtility.ToByteArray(ccstr);
var sha = GetHashAlgorithm();
var ccHashed = sha.ComputeHash(cc);
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16);
}
/// <summary>
/// Create 16 bytes of random salt
/// </summary>
public static byte[] CreateRandomSalt()
{
byte[] retval = new byte[16];
NetRandom.Instance.NextBytes(retval);
return retval;
}
/// <summary>
/// Create 32 bytes of random ephemeral value
/// </summary>
public static byte[] CreateRandomEphemeral()
{
byte[] retval = new byte[32];
NetRandom.Instance.NextBytes(retval);
return retval;
}
/// <summary>
/// Computer private key (x)
/// </summary>
public static byte[] ComputePrivateKey(string username, string password, byte[] salt)
{
var sha = GetHashAlgorithm();
byte[] tmp = Encoding.UTF8.GetBytes(username + ":" + password);
byte[] innerHash = sha.ComputeHash(tmp);
byte[] total = new byte[innerHash.Length + salt.Length];
Buffer.BlockCopy(salt, 0, total, 0, salt.Length);
Buffer.BlockCopy(innerHash, 0, total, salt.Length, innerHash.Length);
// x ie. H(salt || H(username || ":" || password))
return new NetBigInteger(NetUtility.ToHexString(sha.ComputeHash(total)), 16).ToByteArrayUnsigned();
}
/// <summary>
/// Creates a verifier that the server can later use to authenticate users later on (v)
/// </summary>
public static byte[] ComputeServerVerifier(byte[] privateKey)
{
NetBigInteger x = new NetBigInteger(NetUtility.ToHexString(privateKey), 16);
// Verifier (v) = g^x (mod N)
var serverVerifier = g.ModPow(x, N);
return serverVerifier.ToByteArrayUnsigned();
}
/// <summary>
/// SHA hash data
/// </summary>
public static byte[] Hash(byte[] data)
{
var sha = GetHashAlgorithm();
return sha.ComputeHash(data);
}
/// <summary>
/// Compute client public ephemeral value (A)
/// </summary>
public static byte[] ComputeClientEphemeral(byte[] clientPrivateEphemeral) // a
{
// A= g^a (mod N)
NetBigInteger a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
NetBigInteger retval = g.ModPow(a, N);
return retval.ToByteArrayUnsigned();
}
/// <summary>
/// Compute server ephemeral value (B)
/// </summary>
public static byte[] ComputeServerEphemeral(byte[] serverPrivateEphemeral, byte[] verifier) // b
{
var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16);
var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16);
// B = kv + g^b (mod N)
var bb = g.ModPow(b, N);
var kv = v.Multiply(k);
var B = (kv.Add(bb)).Mod(N);
return B.ToByteArrayUnsigned();
}
/// <summary>
/// Compute intermediate value (u)
/// </summary>
public static byte[] ComputeU(byte[] clientPublicEphemeral, byte[] serverPublicEphemeral)
{
// u = SHA-1(A || B)
string one = NetUtility.ToHexString(clientPublicEphemeral);
string two = NetUtility.ToHexString(serverPublicEphemeral);
int len = 66; // Math.Max(one.Length, two.Length);
string ccstr = one.PadLeft(len, '0') + two.PadLeft(len, '0');
byte[] cc = NetUtility.ToByteArray(ccstr);
var sha = GetHashAlgorithm();
var ccHashed = sha.ComputeHash(cc);
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16).ToByteArrayUnsigned();
}
/// <summary>
/// Computes the server session value
/// </summary>
public static byte[] ComputeServerSessionValue(byte[] clientPublicEphemeral, byte[] verifier, byte[] udata, byte[] serverPrivateEphemeral)
{
// S = (Av^u) ^ b (mod N)
var A = new NetBigInteger(NetUtility.ToHexString(clientPublicEphemeral), 16);
var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16);
var u = new NetBigInteger(NetUtility.ToHexString(udata), 16);
var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16);
NetBigInteger retval = v.ModPow(u, N).Multiply(A).Mod(N).ModPow(b, N).Mod(N);
return retval.ToByteArrayUnsigned();
}
/// <summary>
/// Computes the client session value
/// </summary>
public static byte[] ComputeClientSessionValue(byte[] serverPublicEphemeral, byte[] xdata, byte[] udata, byte[] clientPrivateEphemeral)
{
// (B - kg^x) ^ (a + ux) (mod N)
var B = new NetBigInteger(NetUtility.ToHexString(serverPublicEphemeral), 16);
var x = new NetBigInteger(NetUtility.ToHexString(xdata), 16);
var u = new NetBigInteger(NetUtility.ToHexString(udata), 16);
var a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
var bx = g.ModPow(x, N);
var btmp = B.Add(N.Multiply(k)).Subtract(bx.Multiply(k)).Mod(N);
return btmp.ModPow(x.Multiply(u).Add(a), N).ToByteArrayUnsigned();
}
/// <summary>
/// Create XTEA symmetrical encryption object from sessionValue
/// </summary>
public static NetXtea CreateEncryption(byte[] sessionValue)
{
var sha = GetHashAlgorithm();
var hash = sha.ComputeHash(sessionValue);
var key = new byte[16];
for(int i=0;i<16;i++)
{
key[i] = hash[i];
for (int j = 1; j < hash.Length / 16; j++)
key[i] ^= hash[i + (j * 16)];
}
return new NetXtea(key);
}
}
}
#define USE_SHA256
using System;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Helper methods for implementing SRP authentication
/// </summary>
public static class NetSRP
{
private static readonly NetBigInteger N = new NetBigInteger("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16);
private static readonly NetBigInteger g = NetBigInteger.Two;
private static readonly NetBigInteger k = ComputeMultiplier();
private static HashAlgorithm GetHashAlgorithm()
{
#if USE_SHA256
// this does not seem to work as of yet
return SHA256.Create();
#else
return SHA1.Create();
#endif
}
/// <summary>
/// Compute multiplier (k)
/// </summary>
private static NetBigInteger ComputeMultiplier()
{
string one = NetUtility.ToHexString(N.ToByteArrayUnsigned());
string two = NetUtility.ToHexString(g.ToByteArrayUnsigned());
string ccstr = one + two.PadLeft(one.Length, '0');
byte[] cc = NetUtility.ToByteArray(ccstr);
var sha = GetHashAlgorithm();
var ccHashed = sha.ComputeHash(cc);
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16);
}
/// <summary>
/// Create 16 bytes of random salt
/// </summary>
public static byte[] CreateRandomSalt()
{
byte[] retval = new byte[16];
NetRandom.Instance.NextBytes(retval);
return retval;
}
/// <summary>
/// Create 32 bytes of random ephemeral value
/// </summary>
public static byte[] CreateRandomEphemeral()
{
byte[] retval = new byte[32];
NetRandom.Instance.NextBytes(retval);
return retval;
}
/// <summary>
/// Computer private key (x)
/// </summary>
public static byte[] ComputePrivateKey(string username, string password, byte[] salt)
{
var sha = GetHashAlgorithm();
byte[] tmp = Encoding.UTF8.GetBytes(username + ":" + password);
byte[] innerHash = sha.ComputeHash(tmp);
byte[] total = new byte[innerHash.Length + salt.Length];
Buffer.BlockCopy(salt, 0, total, 0, salt.Length);
Buffer.BlockCopy(innerHash, 0, total, salt.Length, innerHash.Length);
return new NetBigInteger(NetUtility.ToHexString(sha.ComputeHash(total)), 16).ToByteArrayUnsigned();
}
/// <summary>
/// Creates a verifier that the server can later use to authenticate users later on (v)
/// </summary>
public static byte[] ComputeServerVerifier(byte[] privateKey)
{
NetBigInteger x = new NetBigInteger(NetUtility.ToHexString(privateKey), 16);
// Verifier (v) = g^x (mod N)
var serverVerifier = g.ModPow(x, N);
return serverVerifier.ToByteArrayUnsigned();
}
/// <summary>
/// SHA hash data
/// </summary>
public static byte[] Hash(byte[] data)
{
var sha = GetHashAlgorithm();
return sha.ComputeHash(data);
}
/// <summary>
/// Compute client public ephemeral value (A)
/// </summary>
public static byte[] ComputeClientEphemeral(byte[] clientPrivateEphemeral) // a
{
// A= g^a (mod N)
NetBigInteger a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
NetBigInteger retval = g.ModPow(a, N);
return retval.ToByteArrayUnsigned();
}
/// <summary>
/// Compute server ephemeral value (B)
/// </summary>
public static byte[] ComputeServerEphemeral(byte[] serverPrivateEphemeral, byte[] verifier) // b
{
var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16);
var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16);
// B = kv + g^b (mod N)
var bb = g.ModPow(b, N);
var kv = v.Multiply(k);
var B = (kv.Add(bb)).Mod(N);
return B.ToByteArrayUnsigned();
}
/// <summary>
/// Compute intermediate value (u)
/// </summary>
public static byte[] ComputeU(byte[] clientPublicEphemeral, byte[] serverPublicEphemeral)
{
// u = SHA-1(A || B)
string one = NetUtility.ToHexString(clientPublicEphemeral);
string two = NetUtility.ToHexString(serverPublicEphemeral);
int len = 66;
string ccstr = one.PadLeft(len, '0') + two.PadLeft(len, '0');
byte[] cc = NetUtility.ToByteArray(ccstr);
var sha = GetHashAlgorithm();
var ccHashed = sha.ComputeHash(cc);
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16).ToByteArrayUnsigned();
}
/// <summary>
/// Computes the server session value
/// </summary>
public static byte[] ComputeServerSessionValue(byte[] clientPublicEphemeral, byte[] verifier, byte[] udata, byte[] serverPrivateEphemeral)
{
// S = (Av^u) ^ b (mod N)
var A = new NetBigInteger(NetUtility.ToHexString(clientPublicEphemeral), 16);
var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16);
var u = new NetBigInteger(NetUtility.ToHexString(udata), 16);
var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16);
NetBigInteger retval = v.ModPow(u, N).Multiply(A).Mod(N).ModPow(b, N).Mod(N);
return retval.ToByteArrayUnsigned();
}
/// <summary>
/// Computes the client session value
/// </summary>
public static byte[] ComputeClientSessionValue(byte[] serverPublicEphemeral, byte[] xdata, byte[] udata, byte[] clientPrivateEphemeral)
{
// (B - kg^x) ^ (a + ux) (mod N)
var B = new NetBigInteger(NetUtility.ToHexString(serverPublicEphemeral), 16);
var x = new NetBigInteger(NetUtility.ToHexString(xdata), 16);
var u = new NetBigInteger(NetUtility.ToHexString(udata), 16);
var a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
var bx = g.ModPow(x, N);
var btmp = B.Add(N.Multiply(k)).Subtract(bx.Multiply(k)).Mod(N);
return btmp.ModPow(x.Multiply(u).Add(a), N).ToByteArrayUnsigned();
}
/// <summary>
/// Create XTEA symmetrical encryption object from sessionValue
/// </summary>
public static NetXtea CreateEncryption(byte[] sessionValue)
{
var sha = GetHashAlgorithm();
var hash = sha.ComputeHash(sessionValue);
var key = new byte[16];
for(int i=0;i<16;i++)
{
key[i] = hash[i];
for (int j = 1; j < hash.Length / 16; j++)
key[i] ^= hash[i + (j * 16)];
}
return new NetXtea(key);
}
}
}

View File

@@ -1,30 +1,30 @@
using System;
namespace Lidgren.Network
{
/// <summary>
/// Result of a SendMessage call
/// </summary>
public enum NetSendResult
{
/// <summary>
/// Message failed to enqueue because there is no connection
/// </summary>
FailedNotConnected = 0,
/// <summary>
/// Message was immediately sent
/// </summary>
Sent = 1,
/// <summary>
/// Message was queued for delivery
/// </summary>
Queued = 2,
/// <summary>
/// Message was dropped immediately since too many message were queued
/// </summary>
Dropped = 3
}
}
using System;
namespace Lidgren.Network
{
/// <summary>
/// Result of a SendMessage call
/// </summary>
public enum NetSendResult
{
/// <summary>
/// Message failed to enqueue because there is no connection
/// </summary>
FailedNotConnected = 0,
/// <summary>
/// Message was immediately sent
/// </summary>
Sent = 1,
/// <summary>
/// Message was queued for delivery
/// </summary>
Queued = 2,
/// <summary>
/// Message was dropped immediately since too many message were queued
/// </summary>
Dropped = 3
}
}

View File

@@ -1,19 +1,19 @@
using System;
namespace Lidgren.Network
{
internal abstract class NetSenderChannelBase
{
// access this directly to queue things in this channel
internal NetQueue<NetOutgoingMessage> m_queuedSends;
internal abstract int WindowSize { get; }
internal abstract int GetAllowedSends();
internal abstract NetSendResult Enqueue(NetOutgoingMessage message);
internal abstract void SendQueuedMessages(float now);
internal abstract void Reset();
internal abstract void ReceiveAcknowledge(float now, int sequenceNumber);
}
}
using System;
namespace Lidgren.Network
{
internal abstract class NetSenderChannelBase
{
// access this directly to queue things in this channel
internal NetQueue<NetOutgoingMessage> m_queuedSends;
internal abstract int WindowSize { get; }
internal abstract int GetAllowedSends();
internal abstract NetSendResult Enqueue(NetOutgoingMessage message);
internal abstract void SendQueuedMessages(float now);
internal abstract void Reset();
internal abstract void ReceiveAcknowledge(float now, int sequenceNumber);
}
}

View File

@@ -1,70 +1,70 @@
using System;
using System.Collections.Generic;
namespace Lidgren.Network
{
/// <summary>
/// Specialized version of NetPeer used for "server" peers
/// </summary>
public class NetServer : NetPeer
{
/// <summary>
/// NetServer constructor
/// </summary>
public NetServer(NetPeerConfiguration config)
: base(config)
{
config.AcceptIncomingConnections = true;
}
/// <summary>
/// Send a message to all connections
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="method">How to deliver the message</param>
public void SendToAll(NetOutgoingMessage msg, NetDeliveryMethod method)
{
var all = this.Connections;
if (all.Count <= 0)
return;
SendMessage(msg, all, method, 0);
}
/// <summary>
/// Send a message to all connections except one
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="method">How to deliver the message</param>
/// <param name="except">Don't send to this particular connection</param>
/// <param name="sequenceChannel">Which sequence channel to use for the message</param>
public void SendToAll(NetOutgoingMessage msg, NetConnection except, NetDeliveryMethod method, int sequenceChannel)
{
var all = this.Connections;
if (all.Count <= 0)
return;
if (except == null)
{
SendMessage(msg, all, method, sequenceChannel);
return;
}
List<NetConnection> recipients = new List<NetConnection>(all.Count - 1);
foreach (var conn in all)
if (conn != except)
recipients.Add(conn);
if (recipients.Count > 0)
SendMessage(msg, recipients, method, sequenceChannel);
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
return "[NetServer " + ConnectionsCount + " connections]";
}
}
}
using System;
using System.Collections.Generic;
namespace Lidgren.Network
{
/// <summary>
/// Specialized version of NetPeer used for "server" peers
/// </summary>
public class NetServer : NetPeer
{
/// <summary>
/// NetServer constructor
/// </summary>
public NetServer(NetPeerConfiguration config)
: base(config)
{
config.AcceptIncomingConnections = true;
}
/// <summary>
/// Send a message to all connections
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="method">How to deliver the message</param>
public void SendToAll(NetOutgoingMessage msg, NetDeliveryMethod method)
{
var all = this.Connections;
if (all.Count <= 0)
return;
SendMessage(msg, all, method, 0);
}
/// <summary>
/// Send a message to all connections except one
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="method">How to deliver the message</param>
/// <param name="except">Don't send to this particular connection</param>
/// <param name="sequenceChannel">Which sequence channel to use for the message</param>
public void SendToAll(NetOutgoingMessage msg, NetConnection except, NetDeliveryMethod method, int sequenceChannel)
{
var all = this.Connections;
if (all.Count <= 0)
return;
if (except == null)
{
SendMessage(msg, all, method, sequenceChannel);
return;
}
List<NetConnection> recipients = new List<NetConnection>(all.Count - 1);
foreach (var conn in all)
if (conn != except)
recipients.Add(conn);
if (recipients.Count > 0)
SendMessage(msg, recipients, method, sequenceChannel);
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
public override string ToString()
{
return "[NetServer " + ConnectionsCount + " connections]";
}
}
}

View File

@@ -1,18 +1,18 @@
using System;
namespace Lidgren.Network
{
internal struct NetStoredReliableMessage
{
public int NumSent;
public float LastSent;
public NetOutgoingMessage Message;
public void Reset()
{
NumSent = 0;
LastSent = 0;
Message = null;
}
}
}
using System;
namespace Lidgren.Network
{
internal struct NetStoredReliableMessage
{
public int NumSent;
public float LastSent;
public NetOutgoingMessage Message;
public void Reset()
{
NumSent = 0;
LastSent = 0;
Message = null;
}
}
}

View File

@@ -1,61 +1,61 @@
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define IS_STOPWATCH_AVAILABLE
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace Lidgren.Network
{
/// <summary>
/// Time service
/// </summary>
public static class NetTime
{
#if IS_STOPWATCH_AVAILABLE
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
#else
private static readonly uint s_timeInitialized = (uint)Environment.TickCount;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
#endif
/// <summary>
/// Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds)
/// </summary>
public static string ToReadable(double seconds)
{
if (seconds > 60)
return TimeSpan.FromSeconds(seconds).ToString();
return (seconds * 1000.0).ToString("N2") + " ms";
}
}
/* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define IS_STOPWATCH_AVAILABLE
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace Lidgren.Network
{
/// <summary>
/// Time service
/// </summary>
public static class NetTime
{
#if IS_STOPWATCH_AVAILABLE
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
#else
private static readonly uint s_timeInitialized = (uint)Environment.TickCount;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
#endif
/// <summary>
/// Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds)
/// </summary>
public static string ToReadable(double seconds)
{
if (seconds > 60)
return TimeSpan.FromSeconds(seconds).ToString();
return (seconds * 1000.0).ToString("N2") + " ms";
}
}
}

View File

@@ -1,19 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
// replace with BCL 4.0 Tuple<> when appropriate
internal struct NetTuple<A, B>
{
public A Item1;
public B Item2;
public NetTuple(A item1, B item2)
{
Item1 = item1;
Item2 = item2;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Lidgren.Network
{
// replace with BCL 4.0 Tuple<> when appropriate
internal struct NetTuple<A, B>
{
public A Item1;
public B Item2;
public NetTuple(A item1, B item2)
{
Item1 = item1;
Item2 = item2;
}
}
}

View File

@@ -1,194 +1,191 @@
using System;
using System.IO;
using System.Xml;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// UPnP support class
/// </summary>
public class NetUPnP
{
private const int c_discoveryTimeOutMillis = 1000;
private string m_serviceUrl;
private NetPeer m_peer;
private ManualResetEvent m_discoveryComplete = new ManualResetEvent(false);
/// <summary>
/// NetUPnP constructor
/// </summary>
public NetUPnP(NetPeer peer)
{
m_peer = peer;
}
internal void Discover(NetPeer peer)
{
string str =
"M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"ST:upnp:rootdevice\r\n" +
"MAN:\"ssdp:discover\"\r\n" +
"MX:3\r\n\r\n";
byte[] arr = System.Text.Encoding.UTF8.GetBytes(str);
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
peer.RawSend(arr, 0, arr.Length, new IPEndPoint(IPAddress.Broadcast, 1900));
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
// allow some extra time for router to respond
// System.Threading.Thread.Sleep(50);
}
internal void ExtractServiceUrl(string resp)
{
#if !DEBUG
try
{
#endif
XmlDocument desc = new XmlDocument();
desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream());
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
if (!typen.Value.Contains("InternetGatewayDevice"))
return;
XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr);
if (node == null)
return;
m_serviceUrl = CombineUrls(resp, node.Value);
m_peer.LogDebug("UPnP service ready");
m_discoveryComplete.Set();
#if !DEBUG
}
catch { return; }
#endif
}
private static string CombineUrls(string gatewayURL, string subURL)
{
// Is Control URL an absolute URL?
if ((subURL.Contains("http:")) || (subURL.Contains(".")))
return subURL;
gatewayURL = gatewayURL.Replace("http://", ""); // strip any protocol
int n = gatewayURL.IndexOf("/");
if (n != -1)
gatewayURL = gatewayURL.Substring(0, n); // Use first portion of URL
return "http://" + gatewayURL + subURL;
}
/// <summary>
/// Add a forwarding rule to the router using UPnP
/// </summary>
public bool ForwardPort(int port, string description)
{
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
return false;
IPAddress mask;
var client = NetUtility.GetMyAddress(out mask);
if (client == null)
return false;
try
{
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
"<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + port.ToString() + "</NewExternalPort>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
"<NewInternalPort>" + port.ToString() + "</NewInternalPort>" +
"<NewInternalClient>" + client.ToString() + "</NewInternalClient>" +
"<NewEnabled>1</NewEnabled>" +
"<NewPortMappingDescription>" + description + "</NewPortMappingDescription>" +
"<NewLeaseDuration>0</NewLeaseDuration>" +
"</u:AddPortMapping>",
"AddPortMapping");
m_peer.LogDebug("Sent UPnP port forward request");
System.Threading.Thread.Sleep(50);
}
catch (Exception ex)
{
m_peer.LogWarning("UPnP port forward failed: " + ex.Message);
return false;
}
return true;
}
/// <summary>
/// Delete a forwarding rule from the router using UPnP
/// </summary>
public bool DeleteForwardingRule(int port)
{
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
return false;
try
{
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
"<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"<NewRemoteHost>" +
"</NewRemoteHost>" +
"<NewExternalPort>" + port + "</NewExternalPort>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
"</u:DeletePortMapping>", "DeletePortMapping");
return true;
}
catch (Exception ex)
{
m_peer.LogWarning("UPnP delete forwarding rule failed: " + ex.Message);
return false;
}
}
/// <summary>
/// Retrieve the extern ip using UPnP
/// </summary>
public IPAddress GetExternalIP()
{
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
return null;
try
{
XmlDocument xdoc = SOAPRequest(m_serviceUrl, "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"</u:GetExternalIPAddress>", "GetExternalIPAddress");
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value;
return IPAddress.Parse(IP);
}
catch (Exception ex)
{
m_peer.LogWarning("Failed to get external IP: " + ex.Message);
return null;
}
}
private XmlDocument SOAPRequest(string url, string soap, string function)
{
string req = "<?xml version=\"1.0\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
soap +
"</s:Body>" +
"</s:Envelope>";
WebRequest r = HttpWebRequest.Create(url);
r.Method = "POST";
byte[] b = System.Text.Encoding.UTF8.GetBytes(req);
r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\"");
r.ContentType = "text/xml; charset=\"utf-8\"";
r.ContentLength = b.Length;
r.GetRequestStream().Write(b, 0, b.Length);
XmlDocument resp = new XmlDocument();
WebResponse wres = r.GetResponse();
Stream ress = wres.GetResponseStream();
resp.Load(ress);
return resp;
}
}
using System;
using System.IO;
using System.Xml;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// UPnP support class
/// </summary>
public class NetUPnP
{
private const int c_discoveryTimeOutMillis = 1000;
private string m_serviceUrl;
private NetPeer m_peer;
private ManualResetEvent m_discoveryComplete = new ManualResetEvent(false);
/// <summary>
/// NetUPnP constructor
/// </summary>
public NetUPnP(NetPeer peer)
{
m_peer = peer;
}
internal void Discover(NetPeer peer)
{
string str =
"M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"ST:upnp:rootdevice\r\n" +
"MAN:\"ssdp:discover\"\r\n" +
"MX:3\r\n\r\n";
byte[] arr = System.Text.Encoding.UTF8.GetBytes(str);
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
peer.RawSend(arr, 0, arr.Length, new IPEndPoint(IPAddress.Broadcast, 1900));
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
}
internal void ExtractServiceUrl(string resp)
{
#if !DEBUG
try
{
#endif
XmlDocument desc = new XmlDocument();
desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream());
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
if (!typen.Value.Contains("InternetGatewayDevice"))
return;
XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr);
if (node == null)
return;
m_serviceUrl = CombineUrls(resp, node.Value);
m_peer.LogDebug("UPnP service ready");
m_discoveryComplete.Set();
#if !DEBUG
}
catch { return; }
#endif
}
private static string CombineUrls(string gatewayURL, string subURL)
{
// Is Control URL an absolute URL?
if ((subURL.Contains("http:")) || (subURL.Contains(".")))
return subURL;
gatewayURL = gatewayURL.Replace("http://", ""); // strip any protocol
int n = gatewayURL.IndexOf("/");
if (n != -1)
gatewayURL = gatewayURL.Substring(0, n); // Use first portion of URL
return "http://" + gatewayURL + subURL;
}
/// <summary>
/// Add a forwarding rule to the router using UPnP
/// </summary>
public bool ForwardPort(int port, string description)
{
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
return false;
IPAddress mask;
var client = NetUtility.GetMyAddress(out mask);
if (client == null)
return false;
try
{
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
"<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + port.ToString() + "</NewExternalPort>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
"<NewInternalPort>" + port.ToString() + "</NewInternalPort>" +
"<NewInternalClient>" + client.ToString() + "</NewInternalClient>" +
"<NewEnabled>1</NewEnabled>" +
"<NewPortMappingDescription>" + description + "</NewPortMappingDescription>" +
"<NewLeaseDuration>0</NewLeaseDuration>" +
"</u:AddPortMapping>",
"AddPortMapping");
m_peer.LogDebug("Sent UPnP port forward request");
System.Threading.Thread.Sleep(50);
}
catch (Exception ex)
{
m_peer.LogWarning("UPnP port forward failed: " + ex.Message);
return false;
}
return true;
}
/// <summary>
/// Delete a forwarding rule from the router using UPnP
/// </summary>
public bool DeleteForwardingRule(int port)
{
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
return false;
try
{
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
"<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"<NewRemoteHost>" +
"</NewRemoteHost>" +
"<NewExternalPort>" + port + "</NewExternalPort>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
"</u:DeletePortMapping>", "DeletePortMapping");
return true;
}
catch (Exception ex)
{
m_peer.LogWarning("UPnP delete forwarding rule failed: " + ex.Message);
return false;
}
}
/// <summary>
/// Retrieve the extern ip using UPnP
/// </summary>
public IPAddress GetExternalIP()
{
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
return null;
try
{
XmlDocument xdoc = SOAPRequest(m_serviceUrl, "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"</u:GetExternalIPAddress>", "GetExternalIPAddress");
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value;
return IPAddress.Parse(IP);
}
catch (Exception ex)
{
m_peer.LogWarning("Failed to get external IP: " + ex.Message);
return null;
}
}
private XmlDocument SOAPRequest(string url, string soap, string function)
{
string req = "<?xml version=\"1.0\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
soap +
"</s:Body>" +
"</s:Envelope>";
WebRequest r = HttpWebRequest.Create(url);
r.Method = "POST";
byte[] b = System.Text.Encoding.UTF8.GetBytes(req);
r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\"");
r.ContentType = "text/xml; charset=\"utf-8\"";
r.ContentLength = b.Length;
r.GetRequestStream().Write(b, 0, b.Length);
XmlDocument resp = new XmlDocument();
WebResponse wres = r.GetResponse();
Stream ress = wres.GetResponseStream();
resp.Load(ress);
return resp;
}
}
}

View File

@@ -1,125 +1,120 @@
using System;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// Sender part of Selective repeat ARQ for a particular NetChannel
/// </summary>
internal sealed class NetUnreliableSenderChannel : NetSenderChannelBase
{
private NetConnection m_connection;
private int m_windowStart;
private int m_windowSize;
private int m_sendStart;
private NetBitVector m_receivedAcks;
internal override int WindowSize { get { return m_windowSize; } }
internal NetUnreliableSenderChannel(NetConnection connection, int windowSize)
{
m_connection = connection;
m_windowSize = windowSize;
m_windowStart = 0;
m_sendStart = 0;
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
}
internal override int GetAllowedSends()
{
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % m_windowSize;
NetException.Assert(retval >= 0 && retval <= m_windowSize);
return retval;
}
internal override void Reset()
{
m_receivedAcks.Clear();
m_queuedSends.Clear();
m_windowStart = 0;
m_sendStart = 0;
}
internal override NetSendResult Enqueue(NetOutgoingMessage message)
{
int queueLen = m_queuedSends.Count + 1;
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
if (queueLen > left)
return NetSendResult.Dropped;
m_queuedSends.Enqueue(message);
return NetSendResult.Sent;
}
// call this regularely
internal override void SendQueuedMessages(float now)
{
int num = GetAllowedSends();
if (num < 1)
return;
// queued sends
while (m_queuedSends.Count > 0 && num > 0)
{
NetOutgoingMessage om;
if (m_queuedSends.TryDequeue(out om))
ExecuteSend(now, om);
num--;
}
}
private void ExecuteSend(float now, NetOutgoingMessage message)
{
m_connection.m_peer.VerifyNetworkThread();
int seqNr = m_sendStart;
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
m_connection.QueueSendMessage(message, seqNr);
Interlocked.Decrement(ref message.m_recyclingCount);
if (message.m_recyclingCount <= 0)
m_connection.m_peer.Recycle(message);
return;
}
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
// seqNr is the actual nr received
internal override void ReceiveAcknowledge(float now, int seqNr)
{
// late (dupe), on time or early ack?
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
if (relate < 0)
{
//m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
return; // late/duplicate ack
}
if (relate == 0)
{
//m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);
// ack arrived right on time
NetException.Assert(seqNr == m_windowStart);
m_receivedAcks[m_windowStart] = false;
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
return;
}
// Advance window to this position
m_receivedAcks[seqNr] = true;
while (m_windowStart != seqNr)
{
m_receivedAcks[m_windowStart] = false;
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
}
}
}
using System;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// Sender part of Selective repeat ARQ for a particular NetChannel
/// </summary>
internal sealed class NetUnreliableSenderChannel : NetSenderChannelBase
{
private NetConnection m_connection;
private int m_windowStart;
private int m_windowSize;
private int m_sendStart;
private NetBitVector m_receivedAcks;
internal override int WindowSize { get { return m_windowSize; } }
internal NetUnreliableSenderChannel(NetConnection connection, int windowSize)
{
m_connection = connection;
m_windowSize = windowSize;
m_windowStart = 0;
m_sendStart = 0;
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
}
internal override int GetAllowedSends()
{
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % m_windowSize;
NetException.Assert(retval >= 0 && retval <= m_windowSize);
return retval;
}
internal override void Reset()
{
m_receivedAcks.Clear();
m_queuedSends.Clear();
m_windowStart = 0;
m_sendStart = 0;
}
internal override NetSendResult Enqueue(NetOutgoingMessage message)
{
int queueLen = m_queuedSends.Count + 1;
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
if (queueLen > left)
return NetSendResult.Dropped;
m_queuedSends.Enqueue(message);
return NetSendResult.Sent;
}
// call this regularely
internal override void SendQueuedMessages(float now)
{
int num = GetAllowedSends();
if (num < 1)
return;
// queued sends
while (m_queuedSends.Count > 0 && num > 0)
{
NetOutgoingMessage om;
if (m_queuedSends.TryDequeue(out om))
ExecuteSend(now, om);
num--;
}
}
private void ExecuteSend(float now, NetOutgoingMessage message)
{
m_connection.m_peer.VerifyNetworkThread();
int seqNr = m_sendStart;
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
m_connection.QueueSendMessage(message, seqNr);
Interlocked.Decrement(ref message.m_recyclingCount);
if (message.m_recyclingCount <= 0)
m_connection.m_peer.Recycle(message);
return;
}
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
// seqNr is the actual nr received
internal override void ReceiveAcknowledge(float now, int seqNr)
{
// late (dupe), on time or early ack?
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
if (relate < 0)
return; // late/duplicate ack
if (relate == 0)
{
// ack arrived right on time
NetException.Assert(seqNr == m_windowStart);
m_receivedAcks[m_windowStart] = false;
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
return;
}
// Advance window to this position
m_receivedAcks[seqNr] = true;
while (m_windowStart != seqNr)
{
m_receivedAcks[m_windowStart] = false;
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
}
}
}
}

View File

@@ -1,29 +1,29 @@
using System;
namespace Lidgren.Network
{
internal sealed class NetUnreliableSequencedReceiver : NetReceiverChannelBase
{
private int m_lastReceivedSequenceNumber;
public NetUnreliableSequencedReceiver(NetConnection connection)
: base(connection)
{
}
internal override void ReceiveMessage(NetIncomingMessage msg)
{
int nr = msg.m_sequenceNumber;
// ack no matter what
m_connection.QueueAck(msg.m_receivedMessageType, nr);
int relate = NetUtility.RelativeSequenceNumber(nr, m_lastReceivedSequenceNumber);
if (relate < 0)
return; // drop if late
m_lastReceivedSequenceNumber = nr;
m_peer.ReleaseMessage(msg);
}
}
}
using System;
namespace Lidgren.Network
{
internal sealed class NetUnreliableSequencedReceiver : NetReceiverChannelBase
{
private int m_lastReceivedSequenceNumber;
public NetUnreliableSequencedReceiver(NetConnection connection)
: base(connection)
{
}
internal override void ReceiveMessage(NetIncomingMessage msg)
{
int nr = msg.m_sequenceNumber;
// ack no matter what
m_connection.QueueAck(msg.m_receivedMessageType, nr);
int relate = NetUtility.RelativeSequenceNumber(nr, m_lastReceivedSequenceNumber);
if (relate < 0)
return; // drop if late
m_lastReceivedSequenceNumber = nr;
m_peer.ReleaseMessage(msg);
}
}
}

Some files were not shown because too many files have changed in this diff Show More