From cb61de946926aedf19eb987dcb94a9b612b3f795 Mon Sep 17 00:00:00 2001 From: punkrockguy318 Date: Sun, 23 Oct 2011 23:02:05 +0000 Subject: [PATCH] moving fceu-server to old/, makes more sense than in fceux source tree [[Split portion of a mixed commit.]] --- fceu-server/AUTHORS | 2 + fceu-server/COPYING | 340 +++++++ fceu-server/ChangeLog | 30 + fceu-server/Makefile | 20 + fceu-server/README | 51 ++ fceu-server/dist/fceu-server-0.0.4.tar.gz | Bin 0 -> 17611 bytes fceu-server/dist/fceu-server-0.0.5.tar.gz | Bin 0 -> 18623 bytes fceu-server/dist/fceunetserver-0.0.3.tar.bz2 | Bin 0 -> 16388 bytes fceu-server/fceu-server.conf | 5 + fceu-server/md5.cpp | 266 ++++++ fceu-server/md5.h | 37 + fceu-server/server.cpp | 914 +++++++++++++++++++ fceu-server/throttle.cpp | 102 +++ fceu-server/throttle.h | 23 + fceu-server/types.h | 38 + 15 files changed, 1828 insertions(+) create mode 100644 fceu-server/AUTHORS create mode 100644 fceu-server/COPYING create mode 100644 fceu-server/ChangeLog create mode 100644 fceu-server/Makefile create mode 100644 fceu-server/README create mode 100644 fceu-server/dist/fceu-server-0.0.4.tar.gz create mode 100644 fceu-server/dist/fceu-server-0.0.5.tar.gz create mode 100644 fceu-server/dist/fceunetserver-0.0.3.tar.bz2 create mode 100644 fceu-server/fceu-server.conf create mode 100644 fceu-server/md5.cpp create mode 100644 fceu-server/md5.h create mode 100644 fceu-server/server.cpp create mode 100644 fceu-server/throttle.cpp create mode 100644 fceu-server/throttle.h create mode 100644 fceu-server/types.h diff --git a/fceu-server/AUTHORS b/fceu-server/AUTHORS new file mode 100644 index 00000000..0888cb48 --- /dev/null +++ b/fceu-server/AUTHORS @@ -0,0 +1,2 @@ +Major developer: Xodnizel +Interface changes and all releases past 0.0.3: Lukas Sabota diff --git a/fceu-server/COPYING b/fceu-server/COPYING new file mode 100644 index 00000000..45645b4b --- /dev/null +++ b/fceu-server/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/fceu-server/ChangeLog b/fceu-server/ChangeLog new file mode 100644 index 00000000..89dde3c4 --- /dev/null +++ b/fceu-server/ChangeLog @@ -0,0 +1,30 @@ +0.0.5: + Interface received massive overhaul. Now takes command line + options. This will allow the server to communicate with + other front-ends. Integration can now occur in gfceu. + +0.0.4: + Renamed from "server" to "fceu-server". + Renamed standard.conf -> fceu-standard.conf + If no configuration file is specified, look in /etc and ./ + Added a --help option. + Added GPL headers + +0.0.3: + Error messages during server startup are more verbose(slightly). + + Fixed initialization of the sockaddr_in structure, sin_family + member. It's now initialized to AF_INET before calling bind(). + While not initializing that member seemed to work ok on + Linux 2.4, it won't work on Linux 2.2 and under Cygwin, + and possibly other environments. + +0.0.2: + Switched to using fcntl() to set the socket to nonblocking, + so I don't need to use MSG_NOWAIT in send() anymore, which is + undefined under Cygwin. + + Added a "clean" Makefile target. + +0.0.1: + First Release diff --git a/fceu-server/Makefile b/fceu-server/Makefile new file mode 100644 index 00000000..f30c82e3 --- /dev/null +++ b/fceu-server/Makefile @@ -0,0 +1,20 @@ +PREFIX = /usr +OUTFILE = fceu-server + +CC = g++ +OBJS = server.o md5.o throttle.o + + +all: ${OBJS} + ${CC} -o ${OUTFILE} ${OBJS} + +clean: + rm -f ${OUTFILE} ${OBJS} + +install: + install -m 755 -D fceu-server ${PREFIX}/bin/fceu-server + install -m 644 -D fceu-server.conf /etc/fceu-server.conf + +server.o: server.cpp +md5.o: md5.cpp +throttle.o: throttle.cpp diff --git a/fceu-server/README b/fceu-server/README new file mode 100644 index 00000000..1a4628a2 --- /dev/null +++ b/fceu-server/README @@ -0,0 +1,51 @@ +FCE Ultra Network Play Server v0.0.5 +------------------------------------ + +To compile, type this in the shell: +$ make +To install, type this in this shell: +$ sudo make install +To run, type this in shell: +$ ./fceu-server + +To compile under MS Windows, you should use Cygwin. I'm not +going to change this server to use Win-old-dirty-smelly-sock natively. + +If it doesn't compile, sell your to the +. + +Most beings can run it like "./fceu-server >logfile &". +Windows users can run it some other way. A batch file with absolute paths, perhaps? + snuggums.bat: + C:\somethingdirectory\server.exe c:\somethingdirectory\standard.conf + +With the default settings, each client should use about 65-70Kbps, excluding any +data transferred during chat, state loads, etc(which should be negligible, but limits +will be placed on these types of transfers in the future). + +Clients connecting with high-latency or slow links may use more bandwidth, or they +may use less bandwidth. I'm really not quite sure. If it concerns you, test it. + +Any client connecting over VERY high latency links, such as bidirectional satellite connections, +may find that attempting network play will lock up his/her connection for +several minutes. Right, Disch. ;) + +The server probably won't scale well to a huge number of clients connected at the same time. + +Bumping up the server's priority and running it on a low-latency kernel(preferably with +1 ms or smaller timeslices) should help make network play more usable if you're running the +network play server on an otherwise non-idle physical server. + + + +TODO: + +Implement a more flexible timing system, so that PAL games will be playable. + +Change the protocol to allow the client to specify the size of input update information, +so devices like the powerpad or zapper can work over network play. + +Send emulation info, such as NTSC/PAL, input devices, and Game Genie emulation at connect +time, to make it easier on end users. + +On Mac OSX, server is reporting back to clients "Invalid number of players specified (0)". Running in a linux VM in bridged mode resolved this issue if you need to host the server on OSX. diff --git a/fceu-server/dist/fceu-server-0.0.4.tar.gz b/fceu-server/dist/fceu-server-0.0.4.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..a78507427ed962143258cad3133e09322820d51b GIT binary patch literal 17611 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 WXZD$WW}p9o&wl^|{)wLe7y$qok7hmq literal 0 HcmV?d00001 diff --git a/fceu-server/dist/fceunetserver-0.0.3.tar.bz2 b/fceu-server/dist/fceunetserver-0.0.3.tar.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..8d10822450ad54bdf7e617bf92de0598ee9d77ce GIT binary patch literal 16388 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 S!GN#-i@744C`bveor-|k?&zQZ literal 0 HcmV?d00001 diff --git a/fceu-server/fceu-server.conf b/fceu-server/fceu-server.conf new file mode 100644 index 00000000..9c71a224 --- /dev/null +++ b/fceu-server/fceu-server.conf @@ -0,0 +1,5 @@ +maxclients 100 ; Maximum number of clients +connecttimeout 5 ; Connection(login) timeout +framedivisor 1 ; Frame divisor(eg: 60 / framedivisor updates per second) +port 4046 ; Port to listen on +;password sexybeef diff --git a/fceu-server/md5.cpp b/fceu-server/md5.cpp new file mode 100644 index 00000000..24dfd59f --- /dev/null +++ b/fceu-server/md5.cpp @@ -0,0 +1,266 @@ +/* FCE Ultra Network Play Server + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * */ + +/* + * RFC 1321 compliant MD5 implementation, + * by Christophe Devine ; + * this program is licensed under the GPL. + */ + +/* Modified October 3, 2003, to remove testing code, and add + include of "types.h". + + Added simple MD5 to ASCII string conversion function. + -Xodnizel +*/ + + + + +#include +#include "types.h" +#include "md5.h" + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32) (b)[(i) + 3] << 24 ) \ + | ( (uint32) (b)[(i) + 2] << 16 ) \ + | ( (uint32) (b)[(i) + 1] << 8 ) \ + | ( (uint32) (b)[(i) ] ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8) ( (n) ); \ + (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \ + (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \ + (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \ +} + +void md5_starts( struct md5_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void md5_process( struct md5_context *ctx, uint8 data[64] ) +{ + uint32 A, B, C, D, X[16]; + + GET_UINT32( X[0], data, 0 ); + GET_UINT32( X[1], data, 4 ); + GET_UINT32( X[2], data, 8 ); + GET_UINT32( X[3], data, 12 ); + GET_UINT32( X[4], data, 16 ); + GET_UINT32( X[5], data, 20 ); + GET_UINT32( X[6], data, 24 ); + GET_UINT32( X[7], data, 28 ); + GET_UINT32( X[8], data, 32 ); + GET_UINT32( X[9], data, 36 ); + GET_UINT32( X[10], data, 40 ); + GET_UINT32( X[11], data, 44 ); + GET_UINT32( X[12], data, 48 ); + GET_UINT32( X[13], data, 52 ); + GET_UINT32( X[14], data, 56 ); + GET_UINT32( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ +{ \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P( A, B, C, D, 0, 7, 0xD76AA478 ); + P( D, A, B, C, 1, 12, 0xE8C7B756 ); + P( C, D, A, B, 2, 17, 0x242070DB ); + P( B, C, D, A, 3, 22, 0xC1BDCEEE ); + P( A, B, C, D, 4, 7, 0xF57C0FAF ); + P( D, A, B, C, 5, 12, 0x4787C62A ); + P( C, D, A, B, 6, 17, 0xA8304613 ); + P( B, C, D, A, 7, 22, 0xFD469501 ); + P( A, B, C, D, 8, 7, 0x698098D8 ); + P( D, A, B, C, 9, 12, 0x8B44F7AF ); + P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); + P( B, C, D, A, 11, 22, 0x895CD7BE ); + P( A, B, C, D, 12, 7, 0x6B901122 ); + P( D, A, B, C, 13, 12, 0xFD987193 ); + P( C, D, A, B, 14, 17, 0xA679438E ); + P( B, C, D, A, 15, 22, 0x49B40821 ); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P( A, B, C, D, 1, 5, 0xF61E2562 ); + P( D, A, B, C, 6, 9, 0xC040B340 ); + P( C, D, A, B, 11, 14, 0x265E5A51 ); + P( B, C, D, A, 0, 20, 0xE9B6C7AA ); + P( A, B, C, D, 5, 5, 0xD62F105D ); + P( D, A, B, C, 10, 9, 0x02441453 ); + P( C, D, A, B, 15, 14, 0xD8A1E681 ); + P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); + P( A, B, C, D, 9, 5, 0x21E1CDE6 ); + P( D, A, B, C, 14, 9, 0xC33707D6 ); + P( C, D, A, B, 3, 14, 0xF4D50D87 ); + P( B, C, D, A, 8, 20, 0x455A14ED ); + P( A, B, C, D, 13, 5, 0xA9E3E905 ); + P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); + P( C, D, A, B, 7, 14, 0x676F02D9 ); + P( B, C, D, A, 12, 20, 0x8D2A4C8A ); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P( A, B, C, D, 5, 4, 0xFFFA3942 ); + P( D, A, B, C, 8, 11, 0x8771F681 ); + P( C, D, A, B, 11, 16, 0x6D9D6122 ); + P( B, C, D, A, 14, 23, 0xFDE5380C ); + P( A, B, C, D, 1, 4, 0xA4BEEA44 ); + P( D, A, B, C, 4, 11, 0x4BDECFA9 ); + P( C, D, A, B, 7, 16, 0xF6BB4B60 ); + P( B, C, D, A, 10, 23, 0xBEBFBC70 ); + P( A, B, C, D, 13, 4, 0x289B7EC6 ); + P( D, A, B, C, 0, 11, 0xEAA127FA ); + P( C, D, A, B, 3, 16, 0xD4EF3085 ); + P( B, C, D, A, 6, 23, 0x04881D05 ); + P( A, B, C, D, 9, 4, 0xD9D4D039 ); + P( D, A, B, C, 12, 11, 0xE6DB99E5 ); + P( C, D, A, B, 15, 16, 0x1FA27CF8 ); + P( B, C, D, A, 2, 23, 0xC4AC5665 ); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P( A, B, C, D, 0, 6, 0xF4292244 ); + P( D, A, B, C, 7, 10, 0x432AFF97 ); + P( C, D, A, B, 14, 15, 0xAB9423A7 ); + P( B, C, D, A, 5, 21, 0xFC93A039 ); + P( A, B, C, D, 12, 6, 0x655B59C3 ); + P( D, A, B, C, 3, 10, 0x8F0CCC92 ); + P( C, D, A, B, 10, 15, 0xFFEFF47D ); + P( B, C, D, A, 1, 21, 0x85845DD1 ); + P( A, B, C, D, 8, 6, 0x6FA87E4F ); + P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); + P( C, D, A, B, 6, 15, 0xA3014314 ); + P( B, C, D, A, 13, 21, 0x4E0811A1 ); + P( A, B, C, D, 4, 6, 0xF7537E82 ); + P( D, A, B, C, 11, 10, 0xBD3AF235 ); + P( C, D, A, B, 2, 15, 0x2AD7D2BB ); + P( B, C, D, A, 9, 21, 0xEB86D391 ); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +void md5_update( struct md5_context *ctx, uint8 *input, uint32 length ) +{ + uint32 left, fill; + + if( ! length ) return; + + left = ( ctx->total[0] >> 3 ) & 0x3F; + fill = 64 - left; + + ctx->total[0] += length << 3; + ctx->total[1] += length >> 29; + + ctx->total[0] &= 0xFFFFFFFF; + ctx->total[1] += ctx->total[0] < ( length << 3 ); + + if( left && length >= fill ) + { + memcpy( (void *) (ctx->buffer + left), (void *) input, fill ); + md5_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + md5_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy( (void *) (ctx->buffer + left), (void *) input, length ); + } +} + +static uint8 md5_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void md5_finish( struct md5_context *ctx, uint8 digest[16] ) +{ + uint32 last, padn; + uint8 msglen[8]; + + PUT_UINT32( ctx->total[0], msglen, 0 ); + PUT_UINT32( ctx->total[1], msglen, 4 ); + + last = ( ctx->total[0] >> 3 ) & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md5_update( ctx, md5_padding, padn ); + md5_update( ctx, msglen, 8 ); + + PUT_UINT32( ctx->state[0], digest, 0 ); + PUT_UINT32( ctx->state[1], digest, 4 ); + PUT_UINT32( ctx->state[2], digest, 8 ); + PUT_UINT32( ctx->state[3], digest, 12 ); +} + + +/* Uses a static buffer, so beware of how it's used. */ +char *md5_asciistr(uint8 digest[16]) +{ + static char str[33]; + static char trans[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + int x; + + for(x=0;x<16;x++) + { + str[x*2]=trans[digest[x]&0x0F]; + str[x*2+1]=trans[digest[x]>>4]; + } + return(str); +} diff --git a/fceu-server/md5.h b/fceu-server/md5.h new file mode 100644 index 00000000..6c5d4e87 --- /dev/null +++ b/fceu-server/md5.h @@ -0,0 +1,37 @@ +/* FCE Ultra Network Play Server + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _MD5_H +#define _MD5_H + +struct md5_context +{ + uint32 total[2]; + uint32 state[4]; + uint8 buffer[64]; +}; + +void md5_starts( struct md5_context *ctx ); +void md5_update( struct md5_context *ctx, uint8 *input, uint32 length ); +void md5_finish( struct md5_context *ctx, uint8 digest[16] ); + +/* Uses a static buffer, so beware of how it's used. */ +char *md5_asciistr(uint8 digest[16]); + +#endif /* md5.h */ diff --git a/fceu-server/server.cpp b/fceu-server/server.cpp new file mode 100644 index 00000000..1cdfa5de --- /dev/null +++ b/fceu-server/server.cpp @@ -0,0 +1,914 @@ +/* FCE Ultra Network Play Server + * + * Copyright notice for this file: + * Copyright (C) 2004 Xodnizel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "types.h" +#include "md5.h" +#include "throttle.h" + +#define VERSION "0.0.5" +#define DEFAULT_PORT 4046 +#define DEFAULT_MAX 100 +#define DEFAULT_TIMEOUT 5 +#define DEFAULT_FRAMEDIVISOR 1 +#define DEFAULT_CONFIG "/etc/fceu-server.conf" + +// MSG_NOSIGNAL and SOL_TCP have been depreciated on osx +#ifdef __APPLE__ +#define MSG_NOSIGNAL SO_NOSIGPIPE +#define SOL_TCP IPPROTO_TCP +#endif + +typedef struct { + uint32 id; /* mainly for faster referencing when pointed to from the Games + entries. + */ + char *nickname; + int TCPSocket; + void *game; /* Pointer to the game this player is in. */ + int localplayers; /* The number of local players, 1-4 */ + + time_t timeconnect; /* Time the client made the connection. */ + + /* Variables to handle non-blocking TCP reads. */ + uint8 *nbtcp; + uint32 nbtcphas, nbtcplen; + uint32 nbtcptype; +} ClientEntry; + +typedef struct +{ + uint8 id[16]; /* Unique 128-bit identifier for this game session. */ + uint8 joybuf[5]; /* 4 player data + 1 command byte */ + int MaxPlayers; /* Maximum players for this game */ + ClientEntry *Players[4]; /* Pointers to player data. */ + int IsUnique[4]; /* Set to 1 if player is unique client, 0 + if it's a duplicated entry to handle multiple players + per client. + */ + uint8 ExtraInfo[64]; /* Expansion information to be used in future versions + of FCE Ultra. + */ +} GameEntry; + +typedef struct +{ + unsigned int MaxClients; /* The maximum number of clients to allow. */ + /* You should expect each client to use + 65-70Kbps(not including save state loads) while + connected(and logged in), when using the default + FrameDivisor value of 1. + */ + unsigned int ConnectTimeout; /* How long to wait(in seconds) for the client to provide + login data before disconnecting the client. + */ + unsigned int FrameDivisor; /* The number of updates per second are divided + by this number. 1 = 60 updates/sec(approx), + 2 = 30 updates/sec, etc. */ + unsigned int Port; /* The port to listen on. */ + uint8 *Password; /* The server password. */ +} CONFIG; + +CONFIG ServerConfig; + +int LoadConfigFile(char *fn) +{ + FILE *fp; + ServerConfig.Port = ServerConfig.MaxClients = ServerConfig.ConnectTimeout = ServerConfig.FrameDivisor = ~0; + if(fp=fopen(fn,"rb")) + { + char buf[256]; + while(fgets(buf, 256, fp) > 0) + { + if(!strncasecmp(buf,"maxclients",strlen("maxclients"))) + sscanf(buf,"%*s %d",&ServerConfig.MaxClients); + else if(!strncasecmp(buf,"connecttimeout",strlen("connecttimeout"))) + sscanf(buf,"%*s %d",&ServerConfig.ConnectTimeout); + else if(!strncasecmp(buf,"framedivisor",strlen("framedivisor"))) + sscanf(buf,"%*s %d",&ServerConfig.FrameDivisor); + else if(!strncasecmp(buf,"port",strlen("port"))) + sscanf(buf,"%*s %d",&ServerConfig.Port); + else if(!strncasecmp(buf,"password",strlen("password"))) + { + char *pass = 0; + sscanf(buf,"%*s %as",&pass); + if(pass) + { + struct md5_context md5; + ServerConfig.Password = (uint8 *)malloc(16); + md5_starts(&md5); + md5_update(&md5,(uint8*)pass,strlen(pass)); + md5_finish(&md5,ServerConfig.Password); + puts("Password required to log in."); + } + } + } + } + + else + { + printf("Cannot load configuration file %s.\n", fn); + return(0); + } + + if(~0 == (ServerConfig.Port & ServerConfig.MaxClients & ServerConfig.ConnectTimeout & ServerConfig.FrameDivisor)) + { + puts("Incomplete configuration file"); + return(0); + } + //printf("%d,%d,%d\n",ServerConfig.MaxClients,ServerConfig.ConnectTimeout,ServerConfig.FrameDivisor); + printf("Using configuration file located at %s\n", fn); + return(1); +} + +static ClientEntry *Clients; +static GameEntry *Games; + +static void en32(uint8 *buf, uint32 morp) +{ + buf[0]=morp; + buf[1]=morp>>8; + buf[2]=morp>>16; + buf[3]=morp>>24; +} + +static uint32 de32(uint8 *morp) +{ + return(morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<24)); +} + +static char *CleanNick(char *nick); +static int NickUnique(ClientEntry *client); +static void AddClientToGame(ClientEntry *client, uint8 id[16], uint8 extra[64]) throw(int); +static void SendToAll(GameEntry *game, int cmd, uint8 *data, uint32 len) throw(int); +static void BroadcastText(GameEntry *game, const char *fmt, ...) throw(int); +static void TextToClient(ClientEntry *client, const char *fmt, ...) throw(int); +static void KillClient(ClientEntry *client); + +#define NBTCP_LOGINLEN 0x100 +#define NBTCP_LOGIN 0x200 +#define NBTCP_COMMANDLEN 0x300 +#define NBTCP_COMMAND 0x400 + +#define NBTCP_UPDATEDATA 0x800 + +static void StartNBTCPReceive(ClientEntry *client, uint32 type, uint32 len) +{ + client->nbtcp = (uint8 *)malloc(len); + client->nbtcplen = len; + client->nbtcphas = 0; + client->nbtcptype = type; +} + +static void RedoNBTCPReceive(ClientEntry *client) +{ + client->nbtcphas = 0; +} + +static void EndNBTCPReceive(ClientEntry *client) +{ + free(client->nbtcp); + client->nbtcplen = client->localplayers; + client->nbtcphas = 0; + client->nbtcptype = 0; + client->nbtcp = 0; +} + +static uint8 *MakeMPS(ClientEntry *client) +{ + static uint8 buf[64]; + uint8 *bp = buf; + int x; + GameEntry *game = (GameEntry *)client->game; + + for(x=0;x<4;x++) + { + if(game->Players[x] == client) + { + *bp = '0' + x + 1; + bp++; + *bp = ','; + bp++; + } + } + if(*(bp-1) == ',') bp--; + + *bp = 0; + return(buf); +} + +/* Returns 1 if we are back to normal game mode, 0 if more data is yet to arrive. */ +static int CheckNBTCPReceive(ClientEntry *client) throw(int) +{ + if(!client->nbtcplen) + throw(1); /* Should not happen. */ + int l; + + while(l = recv(client->TCPSocket, client->nbtcp + client->nbtcphas, client->nbtcplen - client->nbtcphas, MSG_NOSIGNAL)) + { + if(l == -1) + { + if(errno == EAGAIN || errno == EWOULDBLOCK) + break; + throw(1); /* Die now. NOW. */ + } + client->nbtcphas += l; + + //printf("Read: %d, %04x, %d, %d\n",l,client->nbtcptype,client->nbtcphas, client->nbtcplen); + + /* We're all full. Yippie. */ + if(client->nbtcphas == client->nbtcplen) + { + uint32 len; + + switch(client->nbtcptype & 0xF00) + { + case NBTCP_UPDATEDATA: + { + GameEntry *game = (GameEntry *)client->game; + int x, wx; + if(client->nbtcp[0] == 0xFF) + { + EndNBTCPReceive(client); + StartNBTCPReceive(client, NBTCP_COMMANDLEN, 5); + return(1); + } + for(x=0,wx=0; x < 4; x++) + { + if(game->Players[x] == client) + { + game->joybuf[x] = client->nbtcp[wx]; + wx++; + } + } + RedoNBTCPReceive(client); + } + return(1); + case NBTCP_COMMANDLEN: + { + uint8 cmd = client->nbtcp[4]; + len = de32(client->nbtcp); + if(len > 200000) /* Sanity check. */ + throw(1); + + //printf("%02x, %d\n",cmd,len); + if(!len && !(cmd&0x80)) + { + SendToAll((GameEntry*)client->game, client->nbtcp[4], 0, 0); + EndNBTCPReceive(client); + StartNBTCPReceive(client,NBTCP_UPDATEDATA,client->localplayers); + } + else if(client->nbtcp[4]&0x80) + { + EndNBTCPReceive(client); + if(len) + StartNBTCPReceive(client,NBTCP_COMMAND | cmd,len); + else /* Woops. Client probably tried to send a text message of 0 length. Or maybe a 0-length cheat file? Better be safe! */ + StartNBTCPReceive(client,NBTCP_UPDATEDATA,client->localplayers); + } + else throw(1); + return(1); + } + case NBTCP_COMMAND: + { + len = client->nbtcplen; + uint32 tocmd = client->nbtcptype & 0xFF; + + if(tocmd == 0x90) /* Text */ + { + char *ma, *ma2; + + ma = (char *) malloc(len + 1); + memcpy(ma, client->nbtcp, len); + ma[len] = 0; + asprintf(&ma2, "<%s> %s",client->nickname,ma); + free(ma); + ma = ma2; + len=strlen(ma); + SendToAll((GameEntry*)client->game, tocmd, (uint8 *)ma, len); + free(ma); + } + else + SendToAll((GameEntry*)client->game, tocmd, client->nbtcp, len); + EndNBTCPReceive(client); + StartNBTCPReceive(client,NBTCP_UPDATEDATA,client->localplayers); + return(1); + } + case NBTCP_LOGINLEN:len = de32(client->nbtcp); + if(len > 1024 || len < (16 + 16 + 64 + 1)) + throw(1); + EndNBTCPReceive(client); + StartNBTCPReceive(client,NBTCP_LOGIN,len); + return(1); + case NBTCP_LOGIN: + { + uint32 len; + uint8 gameid[16]; + uint8 *sexybuf; + uint8 extra[64]; + + len = client->nbtcplen; + sexybuf = client->nbtcp; + + /* Game ID(MD5'd game MD5 and password on client side). */ + memcpy(gameid, sexybuf, 16); + sexybuf += 16; + len -= 16; + + if(ServerConfig.Password) + if(memcmp(ServerConfig.Password,sexybuf,16)) + { + TextToClient(client,"Invalid server password."); + throw(1); + } + sexybuf += 16; + len -= 16; + + memcpy(extra, sexybuf, 64); + sexybuf += 64; + len -= 64; + + client->localplayers = *sexybuf; + if(client->localplayers < 1 || client->localplayers > 4) + { + TextToClient(client,"Invalid number(%d) of local players!",client->localplayers); + throw(1); + } + sexybuf++; + len -= 1; + + AddClientToGame(client, gameid, extra); + /* Get the nickname */ + if(len) + { + client->nickname = (char *)malloc(len + 1); + memcpy(client->nickname, sexybuf, len); + client->nickname[len] = 0; + if((client->nickname = CleanNick(client->nickname))) + if(!NickUnique(client)) /* Nickname already exists */ + { + free(client->nickname); + client->nickname = 0; + } + } + uint8 *mps = MakeMPS(client); + + if(!client->nickname) + asprintf(&client->nickname,"*Player %s",mps); + + printf("Client %d assigned to game %d as player %s <%s>\n",client->id,(GameEntry*)client->game - Games,mps, client->nickname); + + int x; + GameEntry *tg=(GameEntry *)client->game; + + for(x=0; xMaxPlayers; x++) + if(tg->Players[x] && tg->IsUnique[x]) + { + if(tg->Players[x] != client) + { + try + { + TextToClient(tg->Players[x], "* Player %s has just connected as: %s",MakeMPS(client),client->nickname); + } catch(int i) + { + KillClient(tg->Players[x]); + } + TextToClient(client, "* Player %s is already connected as: %s",MakeMPS(tg->Players[x]),tg->Players[x]->nickname); + } + else + TextToClient(client, "* You(Player %s) have just connected as: %s",MakeMPS(client),client->nickname); + } + } + EndNBTCPReceive(client); + StartNBTCPReceive(client,NBTCP_UPDATEDATA,client->localplayers); + return(1); + } + } + } + return(0); +} + +int ListenSocket; + +static char *CleanNick(char *nick) +{ + int x; + + for(x=0; x' || nick[x] == '*' || (nick[x] < 0x20)) + nick[x] = 0; + if(!strlen(nick)) + { + free(nick); + nick = 0; + } + + return(nick); +} + +static int NickUnique(ClientEntry *client) +{ + int x; + GameEntry *game = (GameEntry *)client-> game; + + for(x=0; xMaxPlayers; x++) + if(game->Players[x] && client != game->Players[x]) + if(!strcasecmp(client->nickname, game->Players[x]->nickname)) + return(0); + return(1); +} + +static int MakeSendTCP(ClientEntry *client, uint8 *data, uint32 len) throw(int) +{ + if(send(client->TCPSocket, data, len, MSG_NOSIGNAL) != len) + throw(1); + return(1); +} + +static void SendToAll(GameEntry *game, int cmd, uint8 *data, uint32 len) throw(int) +{ + uint8 poo[5]; + int x; + + poo[4] = cmd; + + for(x=0;xMaxPlayers;x++) + { + if(!game->Players[x] || !game->IsUnique[x]) continue; + + try + { + if(cmd & 0x80) + en32(poo, len); + MakeSendTCP(game->Players[x],poo,5); + + if(cmd & 0x80) + { + MakeSendTCP(game->Players[x], data, len); + } + } + catch(int i) + { + KillClient(game->Players[x]); + } + + } +} + +static void TextToClient(ClientEntry *client, const char *fmt, ...) throw(int) +{ + char *moo; + va_list ap; + + va_start(ap,fmt); + vasprintf(&moo, fmt, ap); + va_end(ap); + + + uint8 poo[5]; + uint32 len; + + poo[4] = 0x90; + len = strlen(moo); + en32(poo, len); + + MakeSendTCP(client, poo, 5); + MakeSendTCP(client, (uint8*)moo, len); + free(moo); +} + +static void BroadcastText(GameEntry *game, const char *fmt, ...) throw(int) +{ + char *moo; + va_list ap; + + va_start(ap,fmt); + vasprintf(&moo, fmt, ap); + va_end(ap); + + SendToAll(game, 0x90,(uint8 *)moo,strlen(moo)); + free(moo); +} + +static void KillClient(ClientEntry *client) +{ + GameEntry *game; + uint8 *mps; + char *bmsg; + + game = (GameEntry *)client->game; + if(game) + { + int w; + int tc = 0; + + for(w=0;wMaxPlayers;w++) + if(game->Players[w]) tc++; + + for(w=0;wMaxPlayers;w++) + if(game->Players[w]) + if(game->Players[w] == client) + game->Players[w] = NULL; + + time_t curtime = time(0); + printf("Player <%s> disconnected from game %d on %s",client->nickname,game-Games,ctime(&curtime)); + asprintf(&bmsg, "* Player %s <%s> left.",MakeMPS(client),client->nickname); + if(tc == client->localplayers) /* If total players for this game = total local + players for this client, destroy the game. + */ + { + printf("Game %d destroyed.\n",game-Games); + memset(game, 0, sizeof(GameEntry)); + game = 0; + } + } + else + { + time_t curtime = time(0); + printf("Unassigned client %d disconnected on %s",client->id, ctime(&curtime)); + } + if(client->nbtcp) + free(client->nbtcp); + + if(client->nickname) + free(client->nickname); + + if(client->TCPSocket != -1) + close(client->TCPSocket); + memset(client, 0, sizeof(ClientEntry)); + client->TCPSocket = -1; + + if(game) + BroadcastText(game,"%s",bmsg); +} + +static void AddClientToGame(ClientEntry *client, uint8 id[16], uint8 extra[64]) throw(int) +{ + int wg; + GameEntry *game,*fegame; + + retry: + + game = NULL; + fegame = NULL; + + /* First, find an available game. */ + for(wg=0; wgMaxPlayers = 4; + memcpy(game->id, id, 16); + memcpy(game->ExtraInfo, extra, 64); + } + + + int n; + for(n = 0; n < game->MaxPlayers; n++) + if(game->Players[n]) + { + try + { + uint8 b[5]; + b[4] = 0x81; + MakeSendTCP(game->Players[n], b, 5); + break; + } + catch(int i) + { + KillClient(game->Players[n]); + /* All right, now the client we sent the request to is killed. I HOPE YOU'RE HAPPY! */ + /* Since killing a client can destroy a game, we'll need to see if the game was trashed. + If it was, then "goto retry", and try again. Ugly, yes. I LIKE UGLY. + */ + if(!game->MaxPlayers) + goto retry; + } + } + + int instancecount = client->localplayers; + + for(n=0; nMaxPlayers; n++) + if(!game->Players[n]) + { + game->Players[n] = client; + if(instancecount == client->localplayers) + game->IsUnique[n] = 1; + else + game->IsUnique[n] = 0; + instancecount --; + if(!instancecount) + break; + } + + /* Game is full. */ + if(n == game->MaxPlayers) + { + for(n=0; nMaxPlayers; n++) + if(game->Players[n] == client) + game->Players[n] = 0; + TextToClient(client, "Sorry, game is full. %d instance(s) tried, %d available.",client->localplayers,client->localplayers - instancecount); + throw(1); + } + + client->game = (void *)game; +} + + + + +int main(int argc, char *argv[]) +{ + + struct sockaddr_in sockin; + socklen_t sockin_len; + int i; + char* pass = 0; + /* If we can't load the default config file, use some defined values */ + if(!LoadConfigFile(DEFAULT_CONFIG)) { + ServerConfig.Port = DEFAULT_PORT; + ServerConfig.MaxClients = DEFAULT_MAX; + ServerConfig.ConnectTimeout = DEFAULT_TIMEOUT; + ServerConfig.FrameDivisor = DEFAULT_FRAMEDIVISOR; + } + char* configfile = 0; + + + for(i=1; ilocalplayers; + + while(CheckNBTCPReceive(client)) {}; + } // catch + catch(int i) + { + KillClient(Games[whichgame].Players[n]); + } + } // A games clients + + /* Now we send the data to all the clients. */ + for(n = 0; n < Games[whichgame].MaxPlayers; n++) + { + if(!Games[whichgame].Players[n] || !Games[whichgame].IsUnique[n]) continue; + try + { + MakeSendTCP(Games[whichgame].Players[n], Games[whichgame].joybuf, 5); + } + catch(int i) + { + KillClient(Games[whichgame].Players[n]); + } + } // A game's clients + } // Games + + SpeedThrottle(); + } // while(1) +} diff --git a/fceu-server/throttle.cpp b/fceu-server/throttle.cpp new file mode 100644 index 00000000..846f3e8f --- /dev/null +++ b/fceu-server/throttle.cpp @@ -0,0 +1,102 @@ +/* FCE Ultra Network Play Server + * + * Copyright notice for this file: + * Copyright (C) 2004 Xodnizel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + + +#include +#include +#include "types.h" +#include "throttle.h" + +#ifdef TODO +THROTTLE *MakeThrottle(uint32 fps) +{ + + +} + +/* Returns 1 if it's time to run, 0 if it's not time yet. */ +int TestThrottle(THROTTLE *throt) +{ + +} + +/* Pass it a pointer to an array of THROTTLE structs */ +/* It will check */ +void DoAllThrottles(THROTTLE *throt) +{ + + +} + +void KillThrottle(THROTTLE *throt) +{ + +} + +#endif + +static uint64 tfreq; +static uint64 desiredfps; + +int32 FCEUI_GetDesiredFPS(void) +{ + //if(PAL) + // return(838977920); // ~50.007 + //else + return(1008307711); // ~60.1 +} + +void RefreshThrottleFPS(int divooder) +{ + desiredfps=(FCEUI_GetDesiredFPS() / divooder)>>8; + tfreq=1000000; + tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ +} + +static uint64 GetCurTime(void) +{ + uint64 ret; + struct timeval tv; + + gettimeofday(&tv,0); + ret=(uint64)tv.tv_sec*1000000; + ret+=tv.tv_usec; + return(ret); +} + +void SpeedThrottle(void) +{ + static uint64 ttime,ltime; + + waiter: + ttime=GetCurTime(); + + if( (ttime-ltime) < (tfreq/desiredfps) ) + { + usleep(1000); + goto waiter; + } + if( (ttime-ltime) >= (tfreq*4/desiredfps)) + ltime=ttime; + else + ltime+=tfreq/desiredfps; +} + diff --git a/fceu-server/throttle.h b/fceu-server/throttle.h new file mode 100644 index 00000000..b8357b56 --- /dev/null +++ b/fceu-server/throttle.h @@ -0,0 +1,23 @@ +/* FCE Ultra Network Play Server + * + * Copyright notice for this file: + * Copyright (C) 2004 Xodnizel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * */ + + +void RefreshThrottleFPS(int divooder); +void SpeedThrottle(void); diff --git a/fceu-server/types.h b/fceu-server/types.h new file mode 100644 index 00000000..7b831930 --- /dev/null +++ b/fceu-server/types.h @@ -0,0 +1,38 @@ +/* FCE Ultra Network Play Server + * + * Copyright notice for this file: + * Copyright (C) 2004 Xodnizel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __FCEU_TYPES +#define __FCEU_TYPES + +#include +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; + +typedef unsigned long long uint64; +typedef long long int64; +#define INLINE inline +#define GINLINE inline + +#endif