--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ D26CD42914DA8AAE00270FD9 /* cmd.c in Sources */ = {isa = PBXBuildFile; fileRef = D26CD42414DA8AAE00270FD9 /* cmd.c */; };
+ D26CD42A14DA8AAE00270FD9 /* compress.c in Sources */ = {isa = PBXBuildFile; fileRef = D26CD42514DA8AAE00270FD9 /* compress.c */; };
+ D26CD42B14DA8AAE00270FD9 /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = D26CD42614DA8AAE00270FD9 /* misc.c */; };
+ D26CD42C14DA8AAE00270FD9 /* proxy.c in Sources */ = {isa = PBXBuildFile; fileRef = D26CD42714DA8AAE00270FD9 /* proxy.c */; };
+ D26CD43014DA8D8700270FD9 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = D26CD42F14DA8D8700270FD9 /* main.c */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ D26CD41014DA8A7700270FD9 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ D26CD41214DA8A7700270FD9 /* cheapo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cheapo; sourceTree = BUILT_PRODUCTS_DIR; };
+ D26CD42414DA8AAE00270FD9 /* cmd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd.c; sourceTree = SOURCE_ROOT; };
+ D26CD42514DA8AAE00270FD9 /* compress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compress.c; sourceTree = SOURCE_ROOT; };
+ D26CD42614DA8AAE00270FD9 /* misc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = SOURCE_ROOT; };
+ D26CD42714DA8AAE00270FD9 /* proxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = proxy.c; sourceTree = SOURCE_ROOT; };
+ D26CD42814DA8AAE00270FD9 /* proxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = proxy.h; sourceTree = SOURCE_ROOT; };
+ D26CD42F14DA8D8700270FD9 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = SOURCE_ROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ D26CD40F14DA8A7700270FD9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ D26CD40714DA8A7700270FD9 = {
+ isa = PBXGroup;
+ children = (
+ D26CD41514DA8A7700270FD9 /* Cheapo */,
+ D26CD41314DA8A7700270FD9 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ D26CD41314DA8A7700270FD9 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ D26CD41214DA8A7700270FD9 /* cheapo */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ D26CD41514DA8A7700270FD9 /* Cheapo */ = {
+ isa = PBXGroup;
+ children = (
+ D26CD42D14DA8AB900270FD9 /* Headers */,
+ D26CD42414DA8AAE00270FD9 /* cmd.c */,
+ D26CD42514DA8AAE00270FD9 /* compress.c */,
+ D26CD42F14DA8D8700270FD9 /* main.c */,
+ D26CD42614DA8AAE00270FD9 /* misc.c */,
+ D26CD42714DA8AAE00270FD9 /* proxy.c */,
+ );
+ path = Cheapo;
+ sourceTree = "<group>";
+ };
+ D26CD42D14DA8AB900270FD9 /* Headers */ = {
+ isa = PBXGroup;
+ children = (
+ D26CD42814DA8AAE00270FD9 /* proxy.h */,
+ );
+ name = Headers;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ D26CD41114DA8A7700270FD9 /* Cheapo */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D26CD41C14DA8A7700270FD9 /* Build configuration list for PBXNativeTarget "Cheapo" */;
+ buildPhases = (
+ D26CD40E14DA8A7700270FD9 /* Sources */,
+ D26CD40F14DA8A7700270FD9 /* Frameworks */,
+ D26CD41014DA8A7700270FD9 /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Cheapo;
+ productName = Cheapo;
+ productReference = D26CD41214DA8A7700270FD9 /* cheapo */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ D26CD40914DA8A7700270FD9 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0500;
+ };
+ buildConfigurationList = D26CD40C14DA8A7700270FD9 /* Build configuration list for PBXProject "Cheapo" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = D26CD40714DA8A7700270FD9;
+ productRefGroup = D26CD41314DA8A7700270FD9 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ D26CD41114DA8A7700270FD9 /* Cheapo */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ D26CD40E14DA8A7700270FD9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D26CD42914DA8AAE00270FD9 /* cmd.c in Sources */,
+ D26CD42A14DA8AAE00270FD9 /* compress.c in Sources */,
+ D26CD42B14DA8AAE00270FD9 /* misc.c in Sources */,
+ D26CD42C14DA8AAE00270FD9 /* proxy.c in Sources */,
+ D26CD43014DA8D8700270FD9 /* main.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ D26CD41A14DA8A7700270FD9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(NATIVE_ARCH_ACTUAL)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ GCC_CHAR_IS_UNSIGNED_CHAR = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.7;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ D26CD41B14DA8A7700270FD9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_CHAR_IS_UNSIGNED_CHAR = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.7;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ D26CD41D14DA8A7700270FD9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = YES;
+ GCC_CHAR_IS_UNSIGNED_CHAR = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
+ PRODUCT_NAME = cheapo;
+ };
+ name = Debug;
+ };
+ D26CD41E14DA8A7700270FD9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = YES;
+ GCC_CHAR_IS_UNSIGNED_CHAR = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
+ PRODUCT_NAME = cheapo;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ D26CD40C14DA8A7700270FD9 /* Build configuration list for PBXProject "Cheapo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D26CD41A14DA8A7700270FD9 /* Debug */,
+ D26CD41B14DA8A7700270FD9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ D26CD41C14DA8A7700270FD9 /* Build configuration list for PBXNativeTarget "Cheapo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D26CD41D14DA8A7700270FD9 /* Debug */,
+ D26CD41E14DA8A7700270FD9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = D26CD40914DA8A7700270FD9 /* Project object */;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:Cheapo.xcodeproj">
+ </FileRef>
+</Workspace>
--- /dev/null
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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.
+
+ This is the MacOS X port of Cheapo. Cheapo is a proxy that can be used to route QuakeWorld network traffic. Additionally, the proxy can modify the data and has features for enhancing gameplay. You can connect to cheapo as if it were a Quakeworld server, and then give it commands that forward you to a real server. The proxy can also be run on a firewall machine incapable of handling Quake traffic, so that machines inside the firewall can be used for playing.
+ Copyright (C) 2013 fruitz-of-dojo
+
+ 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.
+
+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.
+
+ {signature of Ty Coon}, 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 Lesser General
+Public License instead of this License.
--- /dev/null
+# Cheapo - QuakeWorld proxy, Linux build
+#
+# make build ./cheapo
+# make install install to /usr/local/bin (PREFIX=... to change)
+# make clean
+#
+# The Windows build uses Makefile.win (nmake /f Makefile.win).
+
+CC ?= cc
+CFLAGS ?= -O2
+LDLIBS += -lm
+PREFIX ?= /usr/local
+BINDIR ?= $(PREFIX)/bin
+
+SRCS = main.c proxy.c cmd.c misc.c compress.c
+OBJS = $(SRCS:.c=.o)
+
+all: cheapo
+
+cheapo: $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
+
+$(OBJS): proxy.h
+
+install: cheapo
+ install -d $(DESTDIR)$(BINDIR)
+ install -m 755 cheapo $(DESTDIR)$(BINDIR)/cheapo
+
+uninstall:
+ rm -f $(DESTDIR)$(BINDIR)/cheapo
+
+clean:
+ rm -f cheapo $(OBJS)
+
+.PHONY: all install uninstall clean
--- /dev/null
+NODEBUG=1\r
+\r
+!include <win32.mak>\r
+\r
+clflags = -Ox -W3 -nologo -DWIN32\r
+ldflags = $(lflags) $(ldebug)\r
+ldlibs = $(conlibs) wsock32.lib user32.lib\r
+\r
+objs = main.obj compress.obj cmd.obj proxy.obj misc.obj\r
+\r
+all: cheapo.exe\r
+\r
+cheapo.exe: $(objs)\r
+ $(link) $(ldflags) -out:$@ $(objs) $(ldlibs)\r
+\r
+.c.obj:\r
+ $(cc) $(clflags) -c $*.c\r
+\r
+main.c : proxy.h\r
+proxy.c : proxy.h\r
+cmd.c : proxy.h\r
+misc.c : proxy.h\r
+\r
--- /dev/null
+Cheapo Linux Port
+Linux port of classic Quake lagswitch
+copyright 2026, David Polakovic, licensed under GPLv2
+
+
+ABOUT:
+Cheapo is a QuakeWorld proxy written by Sami Tammilehto in 1998–1999. You
+connect to Cheapo as if it were a QuakeWorld server, then tell it which real
+server to forward you to. It can also read and modify the traffic, and it has
+features for enhancing gameplay (reports, location messages, timers).
+
+This repository is a fork of the
+[MacOS X port](https://github.com/fruitz-of-dojo/Quake-Cheapo), changed so
+that it builds and runs cleanly on modern 64-bit Linux. It is still the
+same Cheapo V2.61; no gameplay behaviour has been changed.
+
+
+BUILDING:
+You need a GCC and make, then in the directory run:
+ make
+And then:
+ ./cheapo
+To install it system-wide:
+ sudo make install
+ make PREFIX=$HOME/.local install
+
+
+HOW TO RUN:
+Simply use
+ cheapo -n
+It listens on UDP 27500 and never auto-exit. Then in your QuakeWorld client:
+ connect <proxy-host>:27500
+ say .connect <server-host>:27500
+To list all options run
+ cheapo -?
+Cheapo reads .loc files from, and writes its log files to, the directory it is
+started in.
+
+
+NOTES:
+Cheapo speaks the original QuakeWorld protocol (28) and connects to servers
+as a plain vanilla client, so it works with MVDSV and FTEQW servers, but
+client/server protocol extensions (for example faster chunked downloads)
+are not available through it. Messages it doesn't understand are passed
+through unchanged.
+
+
+CHANGES FROM MacOS X:
+- `proxy.h`: `<unistd.h>` and the prototypes for Cheapo's own `stricmp`,
+ `memicmp`, `strlwr` and `mainloop_low` were included only on macOS. They
+ now apply to every non-Windows system. Without this, GCC 14 and newer
+ refuse to compile the code (implicit function declarations are errors).
+- New GNU `Makefile` with `install`/`clean` targets.
+- The Windows nmake file was renamed from `makefile` to `Makefile.win`,
+ because GNU make reads `makefile` before `Makefile`. `mkwin.bat` now
+ runs `nmake /f Makefile.win`.
+- `mklinux` / `mksparc`: `-lm` moved after the source files, so the link
+ no longer fails with "undefined reference to sqrt".
+
+Tested with gcc version 16.2.1 20260810 (GCC) on Parabola GNU\Linux (x86-64).
+
+
+LICENSE:
+https://www.gnu.org/licenses/gpl-2.0.en.html
+see LICENSE.txt
+Original Cheapo: © 1998–1999 Sami Tammilehto
--- /dev/null
+// Cheapo - a QuakeWorld Proxy
+// Copyright (C) 1998 Sami Tammilehto
+//
+// 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.
+
+#include "proxy.h"
+
+//const char *_first_(void)
+char *_first_(void)
+{
+ static char firststring[]="<<<<FFFFFFFF>>>>";
+// char *firststring="<<<<FFFFFFFF>>>>";
+ return(firststring);
+}
+
+void q_help(User *u,uchar *param) //??
+{
+ if(*param=='.') param++;
+ if(!memicmp(param,"fun",3))
+ {
+ funnyhelp(u);
+ }
+ else if(!memicmp(param,"ascii",5))
+ {
+ int i;
+ for(i=0x01;i<0x20;i+=4)
+ {
+ q_printf(u,"#%02X:%c #%02X:%c #%02X:%c #%02X:%c\n",
+ i+0,i+0,
+ i+1,i+1,
+ i+2,i+2,
+ i+3,i+3);
+ }
+ }
+ else if(!memicmp(param,"con",3))
+ q_print(u,".connect <address>\n"
+ "Connects to a server or a proxy.\n"
+ "If linking to another proxy, the\n"
+ "first proxy in the chain will\n"
+ "respond to .command, and the\n"
+ "last proxy to proxy:command. All\n"
+ "respond to hostname:command.\n"
+ );
+ else if(!memicmp(param,"dis",3))
+ q_print(u,".disconnect\n"
+ "Disconnects from a server or\n"
+ "a proxy.\n"
+ );
+ else if(!memicmp(param,"rec",3))
+ q_print(u,".reconnect\n"
+ "Reconnects to the last address\n"
+ "connected to.\n"
+ );
+ else if(!memicmp(param,"use",3))
+ q_print(u,".users\n"
+ "Lists users connected to this\n"
+ "proxy and the address they are\n"
+ "connected to.\n"
+ );
+ else if(!memicmp(param,"nam",3))
+ q_print(u,".name <name>\n"
+ "This can be used to rename the\n"
+ "proxy, so that it responds to\n"
+ "commands which start <name>:\n"
+ "proxy: or host: is no longer\n"
+ "recognized after this command.\n"
+ );
+ else if(!memicmp(param,"fil",3))
+ q_print(u,".filter <group> <group> ...\n"
+ "This commands allows dividing a\n"
+ "team into groups and sending\n"
+ "messages only to a single group.\n"
+ "All groupnames must be short and\n"
+ "start with #. Here's an example:\n"
+ " player1 .filter #att\n"
+ " player2 .filter #def\n"
+ "After this a message ending with\n"
+ "' #att' like 'say_team go! #att'\n"
+ "is seen only by player 1. Texts\n"
+ "with no group are seen by all,\n"
+ "and people without no filters\n"
+ "see all messages as well. You\n"
+ "can belong to multiple groups,\n"
+ "but a message can only have one\n"
+ "group. All text after '#' is\n"
+ "removed from the message, and\n"
+ "there must be a space before '#'.\n"
+ );
+ else if(!memicmp(param,"tra",3))
+ q_print(u,".track <player>"
+ "Gives a 1st-person tracking view\n"
+ "of <player>. Must be a spectator\n"
+ "and in fly-mode. NOTE: QW 2.3 has\n"
+ "a cl_chasecam 1 option that gives\n"
+ "you tracking without a proxy.\n"
+ );
+ else if(!memicmp(param,"cam",3))
+#ifdef TIMERS
+ q_print(u,".camp <items...>\n"
+ "Selects which items are timed:\n"
+ " none, quad, penta, ring, all\n"
+ "See also .remind and .guess\n"
+ );
+#else
+ q_print(u,".camp <items...>\n"
+ "Timers are no longer available in\n"
+ "Cheapo, and this command is obsolete.\n"
+ );
+#endif
+ else if(!memicmp(param,"rem",3))
+#ifdef TIMERS
+ q_print(u,".remind <time>\n"
+ "Remind <time> seconds before a\n"
+ "powerup appears with a message\n"
+ "near center of the screen. Give\n"
+ "0 to disable remind.\n"
+ );
+#else
+ q_print(u,".remind <time>\n"
+ "Timers are no longer available in\n"
+ "Cheapo, and this command is obsolete.\n"
+ );
+#endif
+ else if(!memicmp(param,"gue",3))
+#ifdef TIMERS
+ q_print(u,".guess <1/0>\n"
+ "Normally camp timers trigger\n"
+ "when somebody picks up a powerup\n"
+ "and you hear it (guess 0). With\n"
+ "guess 1, if you don't hear a\n"
+ "powerup when last counter goes\n"
+ "to zero, the counter is reset\n"
+ "(assuming someone probably picked\n"
+ "the powerup immediately.) The new\n"
+ "counter has a different symbol to\n"
+ "show that the count value is a\n"
+ "guess. Additionally, when someone\n"
+ "says 'i have <powerup>' and you\n"
+ "don't have time for the given\n"
+ "powerup, the counter is reset\n"
+ "with a guess.\n"
+ );
+#else
+ q_print(u,".guess <1/0>\n"
+ "Timers are no longer available in\n"
+ "Cheapo, and this command is obsolete.\n"
+ );
+#endif
+ else if(!memicmp(param,"not",3))
+#ifdef TIMERS
+ q_print(u,".notimers < 1 | 0 >\n"
+ "Disables camp timers. Text 'notimers'\n"
+ "is shown in f_version and .who proxy\n"
+ "when this is active. The purpose is\n"
+ "to allow games where it's easy for\n"
+ "everyone to check that timers are not\n"
+ "used. Notimers mode must be set in the\n"
+ "first proxy if you have linked through\n"
+ "many proxies. You can enable notimers\n"
+ "when connected, but not disable them\n"
+ );
+#else
+ q_print(u,".notimers < 1 | 0 >\n"
+ "Timers are no longer available in\n"
+ "Cheapo, and this command is obsolete.\n"
+ "Notimers is always enabled.\n"
+ );
+#endif
+ else if(!memicmp(param,"flo",3))
+ q_print(u,".flood <1/0>\n"
+ "Flood protect protect. If enabled\n"
+ "the proxy does not allow you to\n"
+ "trigger floodprotect on the server\n"
+ "(instead the proxy gives the flood\n"
+ "protect warning). The advantage is\n"
+ "that you won't get any flooding\n"
+ "penalty. Server limits are assumed\n"
+ "to be 4 messages in 4 sec. Default\n"
+ "enabled.\n"
+ );
+ else if(!memicmp(param,"point2",6))
+ q_print(u,"Pointing makes it possible to\n"
+ "report to teammates an item you\n"
+ "see/pick using following in say:\n"
+// " %T - item pointed to with move\n"
+// " direction for players\n"
+ " %t - item pointed to\n"
+ " %i - last item picked up\n"
+ " %j - last item pointed to\n"
+ " %k - last item picked/pointed\n"
+ " %m - last item picked or nearest\n"
+ " %x - %t but item name only\n"
+ " %y - %t but location only\n"
+ "The nearest object near center\n"
+ "of the screen is picked for point.\n"
+ "Picking only tracks items you can\n"
+ "point.\n"
+ );
+ else if(!memicmp(param,"poi",3))
+ q_print(u,".point <items>\n"
+ "This command controls what objects\n"
+ "are taken into account for %tijk:\n"
+ " quad, penta, ring, player, enemy,\n"
+ " ra, ya, ga, mh, suit, pack,\n"
+ " rl, gl, lg, sng, ng, ssg\n"
+ " cell, rock, nail, shel, health\n"
+ " misc (=rune+key+flag+sentry-etc)\n"
+ "aliases:\n"
+ " none - no items\n"
+ " power - quad penta ring\n"
+ " armor - ra ya ga\n"
+ " all - everything\n"
+ " def - default items:\n"
+ " player, pack, armors, mega, rock\n"
+ " powerups, lg, rl, ga, sng, misc\n"
+ "see .help point2 for more info.\n"
+ );
+ else if(!memicmp(param,"too",3))
+ q_print(u,".took <items>\n"
+ "Select what items are reported\n"
+ "with f_took when you collect them.\n"
+ "Items is a list of:\n"
+ " quad, penta, ring, player, enemy\n"
+ " ra, ya, ga, mh, suit, pack,\n"
+ " rl, gl, lg, sng, ng, ssg\n"
+ " cell, rock, nail, shel, health\n"
+ " misc (=rune+key+flag+sentry-etc)\n"
+ "Default:\n"
+ " armors, lg, rl, gl\n"
+ );
+ else if(!memicmp(param,"rep",3))
+ q_print(u,".report <events>\n"
+ "What events are reported to team\n"
+ "automatically. Possibilites:\n"
+ "none - nothing \n"
+ "reply - when someone says something\n"
+ " containing ':' (as in f_report)\n"
+ " reply with f_report\n"
+ "power - when you pick a powerup\n"
+ " execute f_powerup.\n"
+/*
+ "power2 - allows you to specify separate\n"
+ " commands for each powerup. You"
+ " must define aliases:\n"
+ " f_power_quad, f_power_noquad\n"
+ " f_power_pent, f_power_nopent\n"
+ " f_power_ring, f_power_noring\n"
+*/
+ "took - when you collect an item\n"
+ " execute f_took (see .took)\n"
+#ifdef TIMERS
+ "sync - when you hear anyone pick a\n"
+ " powerup a team message like\n"
+ " 'Quad #1 picked up' is sent.\n"
+ " These messages are used to\n"
+ " sync powerup timers, and are\n"
+ " compatible with Qizmo. NOTE:\n"
+ " Cheapo hides these messages\n"
+ " when you play, but they do\n"
+ " affect the timers.\n"
+ "show - show&sync. Same as previous\n"
+ " except that the picked up\n"
+ " messages are shown. NOTE: if\n"
+ " all players in a team use\n"
+ " sync there will be multiple\n"
+ " messages\n"
+#endif
+ );
+ else if(!memicmp(param,"lin",3))
+ q_print(u,".line <lines | vga | 512 | 640> [center]\n"
+ "Set number of text lines to align\n"
+ "camp timers correctly. Larger\n"
+ "numbers move the texts down.\n"
+ "640 - alias for 640x480 (45)\n"
+ "512 - alias for 512x384 (33)\n"
+ "vga - alias for 320x200 (11)\n"
+ "If the server sends a statusline\n"
+ "(CTF etc) decrease the .line value\n"
+ "by one so the powerups are shown\n"
+ "above the statusline. The final\n"
+ "'center' option centers powerup\n"
+ "timers (default is left aligned)\n"
+ );
+ else if(!memicmp(param,"mov",3))
+ q_print(u,".move <lines>\n"
+ "Move small overlay texts near\n"
+ "center of screen <lines> lines\n"
+ "downward. Mostly useful to get\n"
+ "that Arena FIGHT! away from the\n"
+ "crosshair (try .move 3) or to\n"
+ "move the remainder (see .remind)\n"
+ );
+ else if(!memicmp(param,"ite",3))
+ q_print(u,".items\n"
+ "List important items in current\n"
+ "level and their nearest mark.\n"
+ "Useful for checking how many\n"
+ "given items are in a level and\n"
+ "where for example quad2 is.\n"
+ );
+ else if(!memicmp(param,"ski",3))
+ q_print(u,".skin <myskin> <other>\n"
+ "Overrides skins for all players\n"
+ "so that your team has <myskin>\n"
+ "and other players have <other>.\n"
+ "Default for <other> is base.\n"
+ "This change is not visible to\n"
+ "other players in the server.\n"
+ "Without parameters .skin restores\n"
+ "skins to normal. Skin can also\n"
+ "be specified as * to leave it\n"
+ "unchanged: '.skin * base' forces\n"
+ "base to enemies, but leaves\n"
+ "teammate skins normal.\n"
+ );
+ else if(!memicmp(param,"col",3))
+ q_print(u,".color <mycolor> <othercolor>\n"
+ "Overrides colors for all players\n"
+ "so that your team has <mycolor>\n"
+ "and other players have <other>.\n"
+ "Default for <other> is 0 (white).\n"
+ "This change is not visible to\n"
+ "other players in the server.\n"
+ "Without parameters .color restores\n"
+ "colors to normal. The same color\n"
+ "is used for top and bottom.\n"
+ "Color can also be specified as *\n"
+ "to leave it unchanged: '.color * 0'\n"
+ "forces white to enemies, but leaves\n"
+ "teammate colors normal. Color can\n"
+ "also have two numbers like 3,4 in\n"
+ "which case the first is topcolor\n"
+ "and the second is bottomcolor.\n"
+ );
+ else if(!memicmp(param,"fps",3))
+ q_print(u,".fps a b c d e f g h\n"
+ "Gives more fps by removing or\n"
+ "modifying visible events. There\n"
+ "are 8 options each specified by\n"
+ "a value. If less than 8 values\n"
+ "given others are assumed to be 0\n"
+ "[x] [values of x]\n"
+ "<a> = explosions [0-5]\n"
+ "<b> = powerup glow [0-2]\n"
+ "<c> = muzzle flash [0-2]\n"
+ "<d> = gib/body filter [0-2]\n"
+ "<e> = rocket change [0-1]\n"
+ "<f> = setinfo filter [0-0]\n"
+ "<g> = nail filter [0-4]\n"
+ "<h> = damage filter [0-1]\n"
+ "Try different options to see\n"
+ "what each number means. 0 is\n"
+ "normal in all cases. Here is\n"
+ "an example: .fps 5 1 2 2 1\n"
+ "The f parameter is ignored.\n"
+ );
+ else if(!memicmp(param,"mar",3))
+ q_print(u,".mark [name|delete|reload|auto]\n"
+ "modifies marks that can be used\n"
+ "to tell player location with\n"
+ "say_team %l.\n"
+ "to show nearest: .mark\n"
+ "to create a new mark: .mark <name>\n"
+ "to delete nearest: .mark delete\n"
+ "to reload marks: .mark reload\n"
+ "to autogenerate: .mark auto\n"
+ "Marks are stored in .loc files in\n"
+ "the current directory (files are\n"
+ "compatible with FAQ-proxy). All\n"
+ "commands save the loc file except\n"
+ "autogenerate which replaces current\n"
+ "marks with marks based on important\n"
+ "items (the automarks are not good,\n"
+ "but better than nothing if you are\n"
+ "in a hurry).\n"
+ );
+ else if(!memicmp(param,"wea",3))
+ q_print(u,".weapon <best | none | #: # # # ...>\n"
+ "controls impulse modification\n"
+ "best | 1\n"
+ " For each impulse command, if that\n"
+ " weapon doesn't have ammo or you\n"
+ " don't have the weapon, select the\n"
+ " next best weapon with ammo. For\n"
+ " default impulses the last working\n"
+ " weapon in this list is selected:\n"
+ " 8: 1 2 4 3 5 8\n"
+ " 7: 1 2 4 3 5 7\n"
+ " 6: 1 2 4 3 5 6\n"
+ " 5: 1 2 4 3 5\n"
+ " 4: 1 2 4\n"
+ " 3: 1 2 3\n"
+ " 2: 1 2\n"
+ "none | 0\n"
+ " disable the above impulses\n"
+ " modifications (default)\n"
+ "10: 3 2\n"
+ " define your own impulse sequence\n"
+ " (numbers above are just examples)\n"
+ " This would define impulse 10 to\n"
+ " be the equivalent of an alias\n"
+ " 'impulse 3; wait; impulse 2'.\n"
+ "10:\n"
+ " undefine the above sequences\n"
+ "You can define up to 8 sequences. Also\n"
+ "note that these only work with default\n"
+ "Quake weapons, not for example in TF.\n"
+ );
+ else if(!memicmp(param,"who",3))
+ q_print(u,".who [proxy | fps]\n"
+ "Lists players in the server along\n"
+ "with their teams and skins (like\n"
+ "Kombat Teams who-command). Options:\n"
+ ".who proxy displays what proxies\n"
+ " all players are using\n"
+ ".who fps displays fps rates of\n"
+ " visible players. Last\n"
+ " known rates for far\n"
+ " away players in ( ).\n"
+ );
+ else if(!memicmp(param,"sou",3))
+ q_print(u,".sound [triggerchar]\n"
+ "Selects the soundtrigger character and\n"
+ "enables soundtriggers. If the character\n"
+ "is $ (for example) and a team message\n"
+ "ends like '$items/damage.wav' that sound\n"
+ "will be played. Start of the message\n"
+ "is displayed, so 'help $items/damage.wav'\n"
+ "would print 'help' and play that sound.\n"
+ "Use 'none' to disable (default disabled).\n"
+ );
+#ifdef IDENT
+ else if(!memicmp(param,"ide",3))
+ q_print(u,".ident [format formattext]\n"
+ "Prints who you are looking at above\n"
+ "the poweruptimers. The format can be\n"
+ "changed with for example:\n"
+ ".ident format %N %Aa:%a h:%h\n"
+ "Format needs only be set once, the\n"
+ "default is like f_report. You can\n"
+ "only get status of teammates using\n"
+ "Cheapo 2.2 or later. Otherwise only\n"
+ "the name is shown.\n"
+ );
+#endif
+ else if(!memicmp(param,"sta",3))
+ q_print(u,".stat < what to show ... >\n"
+ "Display communications statistics\n"
+ "between proxy and server. Multiple\n"
+ "options can be specified:\n"
+ "all - show all\n"
+ "ser - server side stuff\n"
+ "ext - extended statistics\n"
+ "ping - ping for 8 last packets\n"
+ "net - netgraph for 30 packets\n"
+ "proxy - server/clientside proxies\n"
+ "reset - reset extended stats\n"
+ );
+ else if(!memicmp(param,"cpp",3))
+ q_print(u,".cpps <packets/sec>\n"
+ "Limit the number of client packets\n"
+ "sent to the server per second. Most\n"
+ "useful when you set cl_maxfps 72 to\n"
+ "get smooth play and then want to cut\n"
+ "the number of packets (with .cpps\n"
+ "50 for example) so your connection\n"
+ "won't overload. Dropping looks like\n"
+ "packet loss in netgraph, but if the\n"
+ ".cpps limit is greater than half\n"
+ "your framerate the loss shouldn't\n"
+ "matter in practice.\n"
+ );
+ else if(!memicmp(param,"fmo",3))
+ q_print(u,".fmodified < 1 | 0 >\n"
+ "Enable or disable f_modified which is\n"
+ "used to verify that no models have\n"
+ "been modified in official games. QWCL\n"
+ "must be started from Cheapo command\n"
+ "line for this to work. (enabled by\n"
+ "default).\n"
+ );
+ else if(!memicmp(param,"dmm",3))
+ q_print(u,".dmm 1 | 2 | 3 | auto\n"
+ "Controls deathmatch mode detection.\n"
+ "In mode 1 weapons are reported in\n"
+ "point/took messages. In other modes\n"
+ "they are not (even when enabled with\n"
+ "with .point or .took). Default value\n"
+ "auto reads dmm from server. Values\n"
+ "1, 2 and 3 can be used to force a\n"
+ "mode. Previous Cheapo versions acted\n"
+ "as if mode 1 was always active.\n"
+ );
+ else if(!memicmp(param,"qui",3))
+ q_print(u,".quiet < 0 | 1 >\n"
+ "Just '.quiet' silences all proxy\n"
+ "messages for 0.5 sec, so appending\n"
+ "it before a list of commands removes\n"
+ "the normal output they generate.\n"
+ "Server messages are always printed.\n"
+ "If you have a longer script, you can\n"
+ "put '.quiet 1' at start and '.quiet 0'\n"
+ "at end. This command doesn't print\n"
+ "anything itself.\n"
+ );
+ else if(!memicmp(param,"mor",3))
+ q_print(u,
+ "- for all commands, the first three\n"
+ " letters are enough\n"
+ "- when first connecting to proxy\n"
+ " alias proxyautoexec is executed.\n"
+ " (you can define it yourself)\n"
+ "- proxy removes all unrecognized\n"
+ " messages starting with '!' or '.'\n"
+ "- you can talk to other players in\n"
+ " the proxy when not connected\n"
+ "- see .help funny for funny names\n"
+ "- proxy supports serverside feature\n"
+ " disables (fpd=xx, as in Qizmo)\n"
+ "- proxy defines f_report, f_powerup\n"
+ "- proxy responds to f_version\n"
+ "- proxy shows Cheapo in userinfo\n"
+ );
+ else if(!memicmp(param,"say2",4))
+ {
+ q_print(u,
+ "additional %-commands supported:\n"
+ "these five are uppercase:\n"
+ " %S = your skin (for tf)\n"
+ " %C = your bottom color\n"
+ " %L = these work like %l,%e,%o\n"
+// " %U = your loc & dir (see %T)\n"
+ " %P = last powerup that ended\n"
+ " %E = except after death they\n"
+ " %O = are frozen for 5 seconds\n"
+ " %r = last reported location\n"
+ "see .point for these:\n"
+ " %t = target pointed to\n"
+// " %T = target pointed to with\n"
+// " move direction for players\n"
+ " %x = like %t but item name only\n"
+ " %y = like %t but location only\n"
+ " %i = last item picked up\n"
+ " %j = last target pointed to\n"
+ " %k = last item picked/pointed\n"
+ " %m = last item picked or nearest\n"
+ "powerup reporting:\n"
+ " %q = powerups of last seen enemy\n"
+ " (or quad if none seen)\n"
+ " %g = soon appearing powerups\n"
+ " (or quad if none appearing)\n"
+ "aliases (redefine if you want):\n"
+ "f_report = %[a] %[h] %[b] %p %n\n"
+ "f_powerup = i have %p\n"
+ "f_took = took %i\n"
+ );
+ }
+ else if(!memicmp(param,"say",3))
+ {
+ q_print(u,
+ "following %-commands supported:\n"
+ "status reporting:\n"
+ " %A = armortype\n"
+ " %a = armor\n"
+ " %[a] = %Aa:%a (with [] if under 50)\n"
+ " %h = health\n"
+ " %[h] = h:%h (with [] if under 50)\n"
+ " %w = current weapon\n"
+ " %[w] = %w (with [] if not good)\n"
+ " %b = best weapon\n"
+ " %[b] = %b (with [] if not good)\n"
+ " %p = powerups you have\n"
+ " %[p] = %p (with [])\n"
+ " %s = report only what is low\n"
+ " %l = nearest location\n"
+ " %d = location you died at\n"
+ " %e = enemies\n"
+ " %o = teammates\n"
+ " %n = send to visible players only\n"
+ " %N = send to ALL but yourself\n"
+ " (only use one of %n or %N)\n"
+ "see .help say2 for more %-commands.\n"
+ );
+ }
+ else
+ {
+ q_print(u,
+ "for nearest proxy use: .cmd\n"
+ "for specific proxy use: host:cmd\n"
+ "for final proxy use: proxy:cmd\n"
+ ".help <command> for more help\n"
+ ".help more for misc info\n"
+ ".help say for %-commands\n"
+ "\n"
+#ifdef TIMERS
+ "[connecting] [camp/hud]\n"
+ "connect line \n"
+ "disconnect camp \n"
+ "reconnect remind \n"
+ "users guess \n"
+ "name move \n"
+ " notimers \n"
+ "[teamplay] \n"
+ "who [misc] \n"
+ "mark fps \n"
+ "skin weapon \n"
+ "color items \n"
+ "point track \n"
+ "took cpps \n"
+ "report flood \n"
+ "sound stat \n"
+ " fmodified \n"
+#else
+ "[connecting] [visibles]\n"
+ "connect fps \n"
+ "disconnect skin \n"
+ "reconnect color \n"
+ "users \n"
+ "name [misc] \n"
+ " who \n"
+ "[messages] items \n"
+ "mark track \n"
+ "report cpps \n"
+ "point move \n"
+ "took stat \n"
+ "sound fmodified \n"
+ "filter flood \n"
+ "dmm weapon \n"
+ " quiet \n"
+#endif
+#ifdef IDENT
+ "ident \n"
+#endif
+ );
+ }
+}
+
+void modifycampflags(uchar *s,int *campflags,uchar *resulttxt,int limitmask,int def)
+{
+ int flags=*campflags;
+
+ while(*s)
+ {
+ int set=1,a;
+
+ if(0)
+ {
+ }
+ else if(!memicmp(s,"all",3))
+ {
+ flags=-1;
+ }
+ else if(!memicmp(s,"none",4))
+ {
+ flags=0;
+ }
+ else if(!memicmp(s,"def",3))
+ {
+ flags|=def;
+ }
+ else if(!memicmp(s,"powe",4))
+ {
+ flags|=CAMP_P|CAMP_Q|CAMP_R;
+ }
+ else if(!memicmp(s,"poin",4))
+ {
+ flags|=CAMP_POINTED;
+ }
+ else if(!memicmp(s,"pack",4))
+ {
+ flags|=CAMP_PACK;
+ }
+ else if(!memicmp(s,"rune",4) || !memicmp(s,"flag",4) || !memicmp(s,"misc",4))
+ {
+ flags|=CAMP_RUNE;
+ }
+ else if(!memicmp(s,"armo",4))
+ {
+ flags|=CAMP_RA|CAMP_YA|CAMP_GA;
+ }
+ else if(!memicmp(s,"play",4))
+ {
+ flags|=CAMP_PLAYER;
+ }
+ else if(!memicmp(s,"enem",4))
+ {
+ flags|=CAMP_ENEMY;
+ }
+ else if(!memicmp(s,"heal",4))
+ {
+ flags|=CAMP_HEALTH;
+ }
+ else if(!memicmp(s,"suit",4))
+ {
+ flags|=CAMP_SUIT;
+ }
+ else if(!memicmp(s,"she",3))
+ {
+ flags|=CAMP_SHELLS;
+ }
+ else if(!memicmp(s,"roc",3))
+ {
+ flags|=CAMP_ROCKETS;
+ }
+ else if(!memicmp(s,"nai",3))
+ {
+ flags|=CAMP_NAILS;
+ }
+ else if(!memicmp(s,"cel",3))
+ {
+ flags|=CAMP_CELLS;
+ }
+ else if(!memicmp(s,"ra",2))
+ {
+ a=CAMP_RA;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"ya",2))
+ {
+ a=CAMP_YA;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"ga",2))
+ {
+ a=CAMP_GA;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"me",2) || !memicmp(s,"mh",2))
+ {
+ a=CAMP_MH;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"rl",2))
+ {
+ a=CAMP_RL;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"gl",2))
+ {
+ a=CAMP_GL;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"lg",2))
+ {
+ a=CAMP_LG;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"ng",2))
+ {
+ a=CAMP_NG;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"sn",2))
+ {
+ a=CAMP_SNG;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"ss",2))
+ {
+ a=CAMP_SSG;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"r",1))
+ {
+ a=CAMP_R;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"p",1))
+ {
+ a=CAMP_P;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+ else if(!memicmp(s,"q",1))
+ {
+ a=CAMP_Q;
+ if(!set) flags&=~a;
+ else flags|=a;
+ }
+
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ }
+
+ *resulttxt=0;
+ flags&=limitmask;
+ if(flags&CAMP_RA) strcat(resulttxt,"ra ");
+ if(flags&CAMP_YA) strcat(resulttxt,"ya ");
+ if(flags&CAMP_GA) strcat(resulttxt,"ga ");
+ if(flags&CAMP_MH) strcat(resulttxt,"mh ");
+ if(flags&CAMP_LG) strcat(resulttxt,"lg ");
+ if(flags&CAMP_RL) strcat(resulttxt,"rl ");
+ if(flags&CAMP_GL) strcat(resulttxt,"gl ");
+ if(flags&CAMP_SNG) strcat(resulttxt,"sng ");
+ if(flags&CAMP_NG) strcat(resulttxt,"ng ");
+ if(flags&CAMP_SSG) strcat(resulttxt,"ssg ");
+ if(flags&CAMP_HEALTH) strcat(resulttxt,"heal ");
+ if(flags&CAMP_NAILS) strcat(resulttxt,"nail ");
+ if(flags&CAMP_CELLS) strcat(resulttxt,"cell ");
+ if(flags&CAMP_SHELLS) strcat(resulttxt,"shel ");
+ if(flags&CAMP_ROCKETS) strcat(resulttxt,"rock ");
+ if(flags&CAMP_SUIT) strcat(resulttxt,"suit ");
+ if(flags&CAMP_R) strcat(resulttxt,"ring ");
+ if(flags&CAMP_P) strcat(resulttxt,"pent ");
+ if(flags&CAMP_Q) strcat(resulttxt,"quad ");
+ if(flags&CAMP_PLAYER) strcat(resulttxt,"player ");
+ if(flags&CAMP_ENEMY) strcat(resulttxt,"enemy ");
+ if(flags&CAMP_RUNE) strcat(resulttxt,"misc ");
+ if(flags&CAMP_PACK) strcat(resulttxt,"pack ");
+ if(flags&CAMP_POINTED) strcat(resulttxt,"pointed ");
+
+ *campflags=flags;
+}
+
+char *nextparam(uchar **sp)
+{
+ static uchar buf[64];
+ uchar *d=buf,*s=*sp;
+
+ while(*s<=32 && *s) s++;
+
+ while(d<buf+60 && *s>32)
+ {
+ *d++=*s++;
+ }
+ *d++=0;
+
+ while(*s<=32 && *s) s++;
+
+ *sp=s;
+ return(buf);
+}
+
+void q_command(User *u,uchar *s)
+{
+ uchar tmp[MAXSTR];
+ uchar buf[MAXSTR];
+
+ logx(LOG_DEBUG,"%s command: %s \n",u->name,formatqstring(s));
+
+#ifndef TIMERS
+ u->campthese=0;
+ u->notimers=1;
+#endif
+
+ if(*s=='!')
+ { // internal commands
+ if(!memicmp(s,"!accept",7))
+ { // accept from server (generated message)
+ u->state=STATE_CONNECTED;
+ //q_print(u,"Connected\n");
+ if(loglevel>=LOG_VERBOSE) logx(LOG_CONSOLE,"Join: %s connected to %s\n",u->name,u->connecthere);
+ u->appendserverbytes=0;
+ u->appendclientbytes=0;
+ q_servercmd(u,"new");
+ }
+ else if(!memicmp(s,"!sendnew",8))
+ {
+ q_servercmd(u,"new");
+ }
+ else if(!memicmp(s,"!reject",7))
+ { // reject from server (generated message)
+ c_killserver(u);
+ u->state=STATE_HERE;
+ q_print(u,"Server rejected connection\n");
+ u->doconnect=0;
+ }
+ else if(!memicmp(s,"!init",5))
+ { // init after connect to proxy
+ //if(u->sequence>0)
+ {
+ extern uchar *version;
+ uchar *p;
+ char buf[16];
+ int i;
+#if !defined(__APPLE__) && !defined(MACOSX)
+ static int crcchecked;
+#endif /* !__APPLE__ && !MACOSX */
+ memset(tmp,0x9e,36);
+ tmp[0]=0x9d;
+ tmp[35]=0x9f;
+ tmp[36]='\n';
+ tmp[37]=0;
+
+ if(!*proxy_name)
+ {
+ gethostname(proxy_name,255);
+ strlwr(proxy_name);
+ }
+ strncpy(buf,proxy_name,10);
+ for(i=0;i<10;i++)
+ {
+ if(buf[i]=='.') buf[i]=0;
+ }
+ buf[10]=0;
+
+ if(u->version==24) p="protocol QuakeWorld 2.3";
+ else if(u->version==23) p="protocol QuakeWorld 2.29";
+ else if(u->version==22) p="protocol QuakeWorld 2.2";
+ else if(u->version==21) p="protocol QuakeWorld 2.1";
+ else p="protocol unrecognized";
+
+ expires_gettime();
+
+ q_print(u,tmp);
+ q_printf(u,
+ "Welcome to %s %s%s\n"
+ "%s\n"
+ "type \'%s:help\' for commands\n"
+ "type \'.help\' for nearest proxy\n"
+ ,versionname,version,versiontype,p,buf);
+ q_print(u,tmp);
+
+ strcpy(u->proxysay,"say proxy:");
+
+ sprintf(u->proxyhostname,"%s:%i",buf,proxy_port);
+
+ sprintf(u->proxysay2,"say %s:",buf);
+
+ strcpy(u->requestformat,"%N %[a] %[h] %[b] %p");
+
+ sprintf(tmpbuf,"name \"%s\"\n",u->name);
+ q_stufftext(u,tmpbuf);
+
+ u->fmodified=1;
+
+ u->state=STATE_HERE;
+ u->usepercentn=1;
+ u->nofversion=0;
+ u->secondcamp=1;
+ u->floodprot=1;
+ u->pointtell=CAMP_DEF;
+ u->autoitookitems=CAMP_DEFTOOK;
+ u->time_connect=u->time;
+ u->fpd=litemode;
+
+ if(u->version>24)
+ {
+ q_print(u,"Since the protocol is unrecognized, the proxy will probably not work!\n");
+ }
+
+ {
+ char ver[16];
+ sprintf(ver,"%s",version);
+ cl_setinfo(u,"Cheapo",ver);
+ }
+
+ q_stufftext(u,"alias f_report \"say_team %[a] %[h] %[b] %p%n\"\n");
+ q_stufftext(u,"alias f_powerup \"say_team i have %p\"\n");
+ q_stufftext(u,"alias f_took \"say_team took %i\"\n");
+ q_stufftext(u,"proxyautoexec\n");
+
+ if(gamespy)
+ {
+ q_stufftext(u,"exec GameSpyC.cfg");
+ }
+ }
+ }
+ else if(!memicmp(s,"!drop",5))
+ { // drop from proxy
+ if(u->state!=STATE_DROPPING)
+ {
+ u->state=STATE_DROPPING;
+ q_printf(u,"Thanks for using %s.\n",versionname);
+ // disconnect server command
+ u->appendserver[u->appendserverbytes++]=0x02;
+ }
+ }
+ else if(!memicmp(s,"!sdisconnect",6))
+ { // server sends disconnect
+ if(loglevel>=LOG_VERBOSE) logx(LOG_CONSOLE,"Drop: %s dropped from %s\n",u->name,u->connecthere);
+ if(u->state!=STATE_DISCONNECTING)
+ {
+ u->state=STATE_DISCONNECTING;
+ u->dropcount=3;
+ }
+ }
+ else if(!memicmp(s,"!sdrop",6))
+ { // drop from server (called when mode is disconnecting)
+ if(u->dropcount>0)
+ {
+ q_servercmd(u,"drop");
+ u->dropcount--;
+ }
+ else
+ {
+ c_killserver(u);
+
+ if(u->servertimeoutmsg)
+ {
+ u->servertimeoutmsg=0;
+ q_print(u,"Server timeout.");
+ }
+
+ q_command(u,"!sdisconnect");
+ u->dropcount=0;
+ u->appendserverbytes=0;
+ u->appendclientbytes=0;
+ memset(&u->toserveraddr,0,sizeof(u->toserveraddr));
+ u->state=STATE_HERE;
+
+ q_stufftext(u,"changing\n");
+ q_print(u,"Server disconnected.");
+
+ u->time_forcedisconnect=0;
+ }
+ }
+ else if(!memicmp(s,"!kill",5))
+ { // real drop
+ c_textaddress(tmp,&u->toclientaddr);
+ if(strcmp(u->name,"<challenge>") && strcmp(u->name,"<status>"))
+ {
+ logx(LOG_CONSOLE,"Disconnect: %s from %s (users %i)\n",u->name,tmp,usernum-1);
+ }
+ c_killuser(u);
+ }
+ else if(!memicmp(s,"!errors",5))
+ { // real drop
+ c_textaddress(tmp,&u->toclientaddr);
+ logx(LOG_CONSOLE,"Net errors (disconnected): %s from %s\n",u->name,tmp);
+ c_killuser(u);
+ }
+ else if(!memicmp(s,"!timeout",5))
+ { // real drop
+ c_textaddress(tmp,&u->toclientaddr);
+ logx(LOG_CONSOLE,"Timeout (disconnected): %s from %s\n",u->name,tmp);
+ c_killuser(u);
+ }
+ else if(!memicmp(s,"!challenge",10))
+ { // received a challenge from server
+ u->havechallenge=1;
+ logx(LOG_DEBUG,"Got challenge %i\n",u->challenge);
+ u->time_nexttry=u->time+100;
+ }
+ else if(!memicmp(s,"!try",4))
+ { // try connecting to a server
+ if(u->firsttry==1)
+ {
+ *tmpbuf=0;
+ extractfield(tmpbuf,u->clientinfo,"spectator\\");
+ if(*tmpbuf=='0' || *tmpbuf==0) u->amspectator=0;
+ else u->amspectator=1;
+
+ if(u->servermainaddr.sin_port)
+ {
+ c_textaddress(tmp,&u->servermainaddr);
+ if(newsocketmode) memcpy(&u->toserveraddr,&u->servermainaddr,sizeof(SOCKADDR_IN));
+ }
+ else
+ {
+ q_printf(u,"Illegal address: %s\n",u->connecthere);
+ u->state=STATE_HERE;
+ return;
+ }
+
+ if(u->response_b)
+ {
+ u->response_b++;
+ if(!u->response_b) u->response_b++;
+ }
+
+ if(u->isreconnect || c_sameaddress(&u->servermainaddr,&u->lastservermainaddr))
+ {
+ u->time_nexttry=u->time+1600; // 1.6 sec delay
+ }
+ else
+ {
+ u->time_nexttry=u->time+300; // 0.3 sec delay
+ }
+ u->lastservermainaddr=u->servermainaddr;
+
+ u->firsttry=2;
+ return;
+ }
+ else if(u->firsttry==2)
+ { // flip client hidden bit to 0
+ if(u->client_hiddenbit)
+ {
+ u->server_sendsecure=1;
+ }
+ else
+ {
+ q_print(u,"Challenging...\n");
+ u->firsttry=0;
+ }
+ u->time_nexttry=u->time+100; // 0.1 sec delay
+ return;
+ }
+ u->isreconnect=0;
+ u->connectingcount--;
+ if(u->challengecount<=0)
+ {
+ q_print(u,"Server not responding.\n");
+ u->state=STATE_HERE;
+ }
+ else if(u->connectingcount<=0)
+ {
+ q_print(u,"Server not responding.\n");
+ u->state=STATE_HERE;
+ }
+ else
+ {
+ if(!u->havechallenge && u->version>=22)
+ { // ask for challenge
+ memset(tmpbuf,255,4);
+ strcpy(tmpbuf+4,"getchallenge\n");
+ if(loglevel>=LOG_DEBUG)
+ {
+ c_hexdump("Connect attempt",tmpbuf,5+strlen(tmpbuf+4),u);
+ }
+ c_sendservermain(u,tmpbuf,4+strlen(tmpbuf+4));
+ u->time_nexttry=u->time+1000;
+ u->challengecount--;
+ }
+ else
+ { // normal connect after that
+ if(u->challenge==challenge)
+ {
+ q_print(u,"Cannot connect to self.\n");
+ u->state=STATE_HERE;
+ return;
+ }
+ q_print(u,"Connecting...\n");
+ memset(tmpbuf,255,4);
+ if(u->version>=22)
+ {
+ sprintf(tmpbuf+4,"connect %i %i %i %s",
+ u->response_a,
+ u->response_b,
+ u->challenge,
+ u->clientinfo);
+ }
+ else
+ {
+ sprintf(tmpbuf+4,"connect %s",
+ u->clientinfo);
+ }
+
+ if(loglevel>=LOG_DEBUG)
+ {
+ c_hexdump("Connect attempt",tmpbuf,5+strlen(tmpbuf+4)+1,u);
+ }
+ c_sendservermain(u,tmpbuf,4+strlen(tmpbuf+4)+1);
+ u->time_nexttry=u->time+3000;
+ }
+ }
+ }
+ else if(!memicmp(s,"!connect",7))
+ {
+ /*
+ lognpackets=200;
+ oldloglevel=loglevel;
+ loglevel=LOG_PACKETS;
+ logx(LOG_CONSOLE,"--------packetdump %i packets--------",lognpackets);
+ */
+
+ c_reopenserver(u);
+
+ u->connectstartsequence=u->sequence;
+ u->havechallenge=0;
+ u->connectingcount=1000000; // be persistent :)
+ u->challengecount=5;
+ u->time_nexttry=0;
+ u->servermainaddr.sin_port=0;
+ u->firsttry=1;
+ u->state=STATE_NAMELOOKUP;
+ u->time_namelookupmsg=u->time+3000;
+ u->time_reskinall=0;
+
+ if(u->isreconnect) q_printf(u,"Reconnecting to %s\n",u->connecthere);
+ else q_printf(u,"Connecting to %s\n",u->connecthere);
+
+#ifndef WIN32
+ mainloop_low(1); // do namelookup now
+#endif
+ }
+ else if(!memicmp(s,"!disconnect",11))
+ {
+ if(u->servermainaddr.sin_port)
+ {
+ u->time_forcedisconnect=u->time+3000; // force a disconnect in 3 sec
+ u->state=STATE_DISCONNECTING;
+ u->linkedserver=0;
+ u->dropcount=3;
+ if(u->compress_client) u->compress_client=0;
+ }
+ }
+ else if(!memicmp(s,"!servertimeout",13))
+ {
+ q_command(u,"!disconnect");
+ u->servertimeoutmsg=1;
+ }
+ }
+ else
+ { // user commands
+ if(!memicmp(s,".teleport",9))
+ { // FAQ proxy sends
+ // do nothing
+ }
+ else if(!memicmp(s,".hel",4))
+ { // help
+ uchar *p;
+ nextparam(&s); // skip command
+ p=nextparam(&s);
+ q_help(u,p);
+ }
+ else if(!memicmp(s,".nam",4))
+ { // proxy name
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ p=nextparam(&s);
+
+ strncpy(buf,p,10);
+ buf[10]=0;
+ sprintf(u->proxysay,"say %s:",buf);
+ sprintf(u->proxysay2,"say %s:",buf);
+ }
+
+ if(strcmp(u->proxysay,u->proxysay2))
+ {
+ q_printf(u,"Proxy %s responds to:\n"
+ "%scommand\n"
+ "%scommand\n"
+ ".command\n",
+ u->proxyhostname,
+ u->proxysay+4,
+ u->proxysay2+4);
+ }
+ else
+ {
+ q_printf(u,"Proxy %s responds to:\n"
+ "%scommand\n"
+ ".command\n",
+ u->proxyhostname,
+ u->proxysay+4);
+ }
+ }
+ else if(!memicmp(s,".use",4))
+ { // users/clients
+ int i;
+ for(i=0;i<usernum;i++)
+ {
+ c_textaddress(tmp,&user[i].servermainaddr);
+ if(user[i].state==STATE_HERE) strcpy(tmp,"idle");
+ q_printf(u,"%i: %-10s %s\n",i,user[i].name,tmp);
+ }
+ }
+ else if(!memicmp(s,".con",4))
+ { // connect
+ uchar *p;
+
+ if(u->state==STATE_NAMELOOKUP)
+ {
+ q_print(u,"Namelookup still in progress.\n");
+ }
+ else if(u->state>=STATE_CONNECTED)
+ {
+ q_command(u,"!disconnect");
+ }
+ else u->state=STATE_HERE;
+
+ nextparam(&s);
+ p=nextparam(&s);
+ if(strlen(p)>1)
+ {
+ strcpy(u->connecthere,p);
+ u->doconnect=10;
+ u->isreconnect=0;
+ }
+ u->specmode=0;
+ u->specentity=0;
+ }
+ else if(!memicmp(s,".dis",4))
+ { // disconnect
+ if(u->state==STATE_NAMELOOKUP)
+ {
+ q_print(u,"Namelookup still in progress.\n");
+ }
+ else if(u->state==STATE_CONNECTED)
+ {
+ c_textaddress(tmp,&u->servermainaddr);
+ q_printf(u,"Disconnecting from %s\n",tmp);
+ q_command(u,"!disconnect");
+ }
+ else u->state=STATE_HERE;
+ memset(&u->lastservermainaddr,0,sizeof(u->lastservermainaddr));
+ }
+ else if(!memicmp(s,".rec",4))
+ { // reconnect
+ if(u->state==STATE_NAMELOOKUP)
+ {
+ q_print(u,"Namelookup still in progress.\n");
+ }
+ else
+ {
+ if(u->state==STATE_CONNECTED)
+ {
+ q_command(u,"!disconnect");
+ }
+ u->doconnect=10;
+ u->isreconnect=1;
+ }
+ }
+ else if(!memicmp(s,".tra",4))
+ { // start tracking
+ uchar *p;
+ if(u->state<STATE_CONNECTED)
+ {
+ q_print(u,"Cannot track, not connected.\n");
+ return;
+ }
+
+ if(!u->amspectator)
+ {
+ q_print(u,"Cannot track, not spectator.\n");
+ return;
+ }
+
+ if(u->client_spectrack)
+ {
+ q_print(u,"Cannot track, must be in fly mode.\n");
+ return;
+ }
+
+ nextparam(&s);
+ p=nextparam(&s);
+ if(*p)
+ {
+ u->specmode=1;
+ u->specentity=0;
+ spectatenext(u,p);
+ if(u->specentity)
+ {
+ q_printf(u,"Tracking \'%s\' enabled.\n",p);
+ q_print(u,"Jump to change player. Fire to stop tracking.\n");
+ }
+ else
+ {
+ spectatenext(u,NULL);
+ q_printf(u,"Tracking enabled but \'%s\' not found.\n",s);
+ q_print(u,"Jump to change player. Fire to stop tracking.\n");
+ }
+ }
+ else
+ {
+ q_print(u,"Tracking enabled.\n");
+ q_print(u,"Jump to change player. Fire to stop tracking.\n");
+ u->specmode=1;
+ u->specentity=0;
+ spectatenext(u,NULL);
+ }
+ }
+ else if(!memicmp(s,".mar",4))
+ { // mark
+ uchar *place;
+ int a;
+ if(u->state!=STATE_CONNECTED)
+ {
+ q_print(u,"Mark unusable, not connected.\n");
+ return;
+ }
+
+ nextparam(&s);
+
+ if(!memicmp(s,"reload",6))
+ {
+ q_loadloc(u,NULL);
+ }
+ else if(!memicmp(s,"auto",4))
+ {
+ static int ordertable[]=
+ {CAMP_RUNE,CAMP_Q,CAMP_P,CAMP_R,CAMP_RA,CAMP_RL,CAMP_YA,
+ CAMP_GA,CAMP_MH,CAMP_GL,CAMP_LG,CAMP_SNG,0};
+ int i,j,c,d,pass,ord;
+ int redflag=-1,bluflag=-1;
+ char markname[64];
+ char markname2[64];
+ Camp *ca,*ca2;
+ u->locations=0;
+
+ // do we have flags?
+ for(i=0;i<u->camps;i++)
+ {
+ ca=&u->camp[i];
+ if(ca->category==CAMP_RUNE)
+ {
+ if(!strcmp(ca->name,"!r")) redflag=i;
+ if(!strcmp(ca->name,"!b")) bluflag=i;
+ }
+ }
+ if(redflag<0 || bluflag<0) redflag=bluflag=-1;
+ else
+ {
+ q_print(u,"Marking based on red/blue flags\n");
+ }
+
+ q_print(u,"Marks replaced with automarks(tm):\n");
+
+ for(pass=0;pass<=2;pass++)
+ { // pass0=single items, 1=dual items, 2=rest of items
+ for(ord=0;ordertable[ord];ord++)
+ {
+ int num,lowerz,upperz,lowercnt,uppercnt;
+
+ c=ordertable[ord];
+
+ num=0;
+ lowerz= 999999;
+ upperz=-999999;
+ // find number of items, upper & lower
+ for(i=0;i<u->camps;i++)
+ {
+ ca=&u->camp[i];
+ if(ca->category&c)
+ {
+ if(ca->pos[2]<lowerz) lowerz=ca->pos[2];
+ if(ca->pos[2]>upperz) upperz=ca->pos[2];
+ num++;
+ }
+ }
+ lowercnt=0;
+ uppercnt=0;
+ // find number of low / high items
+ for(i=0;i<u->camps;i++)
+ {
+ ca=&u->camp[i];
+ if(ca->category&c)
+ {
+ if(abs(ca->pos[2]-lowerz)<64) lowercnt++;
+ if(abs(ca->pos[2]-upperz)<64) uppercnt++;
+ }
+ }
+
+ if(!num) continue;
+ if(pass==0 && num>1) continue;
+ if(pass==1 && num>2) continue;
+
+ // set texts
+ for(i=0;i<u->camps;i++)
+ {
+ ca=&u->camp[i];
+ if(ca->category&c)
+ {
+ for(j=0;j<u->locations;j++)
+ {
+ d=abs(u->loc[j].x-ca->pos[0])*2+
+ abs(u->loc[j].y-ca->pos[1])*2+
+ abs(u->loc[j].z-ca->pos[2]);
+ if(d<7000) break;
+ }
+ if(j==u->locations)
+ {
+ u->loc[u->locations].x=ca->pos[0];
+ u->loc[u->locations].y=ca->pos[1];
+ u->loc[u->locations].z=ca->pos[2];
+
+ *markname=0;
+ if(num==1)
+ {
+ sprintf(markname,"%s",ca->genericname);
+ }
+ else
+ {
+ if(lowercnt==1 && abs(ca->pos[2]-lowerz)<64)
+ sprintf(markname,"lower-%s",ca->genericname);
+
+ if(uppercnt==1 && abs(ca->pos[2]-upperz)<64)
+ sprintf(markname,"upper-%s",ca->genericname);
+
+ if(!markname[0] && *ca->name=='!')
+ sprintf(markname,"%s",ca->genericname);
+
+ if(!markname[0])
+ sprintf(markname,"%s%c",ca->genericname,ca->name[strlen(ca->name)-1]);
+ }
+
+ if(*markname)
+ {
+ *markname2=0;
+ if(redflag>=0)
+ {
+ int dred,dblu;
+ ca2=&u->camp[redflag];
+ dred=abs(ca2->pos[0]-ca->pos[0])*2+
+ abs(ca2->pos[1]-ca->pos[1])*2+
+ abs(ca2->pos[2]-ca->pos[2]);
+ ca2=&u->camp[bluflag];
+ dblu=abs(ca2->pos[0]-ca->pos[0])*2+
+ abs(ca2->pos[1]-ca->pos[1])*2+
+ abs(ca2->pos[2]-ca->pos[2]);
+ if(dblu*3<dred*2)
+ {
+ strcpy(markname2,"blue ");
+ }
+ else if(dred*3<dblu*2)
+ {
+ strcpy(markname2,"red ");
+ }
+ }
+
+ strcat(markname2,markname);
+
+ sprintf(buf,"- %s\n",markname2);
+ q_print(u,buf);
+
+ q_setlocname(u,u->locations,markname2);
+ u->locations++;
+ if(u->locations>=u->locationsmax-16)
+ {
+ q_growlocmax(u,u->locationsmax*2);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if(!memicmp(s,"delete",6))
+ { // delete mark
+ int b;
+ place=findnear(u,u->pos[0],u->pos[1],u->pos[2],&a);
+ if(a!=-1)
+ {
+ q_explode(u,0x04,u->loc[a].x,u->loc[a].y,u->loc[a].z);
+ q_printf(u,"%s deleted\n",place);
+#if 0
+ q_setlocname(u,a,NULL);
+ if(a!=u->locations-1) memmove(u->loc+a,u->loc+a+1,(u->locations-a-1)*sizeof(Location));
+ u->locations--;
+ memset(u->loc+u->locations,0,sizeof(Location));
+#else
+ q_setlocname(u,a,NULL);
+ for(b=a;b<u->locations-1;b++)
+ {
+ memcpy(u->loc+b,u->loc+b+1,sizeof(Location));
+ }
+ u->locations--;
+ memset(u->loc+u->locations,0,sizeof(Location));
+#endif
+
+ q_savelocs(u);
+ }
+ else
+ { // mark not found
+ q_printf(u,"no marks\n",place);
+ }
+ }
+ else if(!*s) // report
+ {
+ place=findnear(u,u->pos[0],u->pos[1],u->pos[2],&a);
+ q_printf(u,"near %s\n",place);
+ u->appendserver[u->appendserverbytes++]=0x17;
+ u->appendserver[u->appendserverbytes++]=0x04;
+ u->appendserver[u->appendserverbytes++]=u->loc[a].x;
+ u->appendserver[u->appendserverbytes++]=u->loc[a].x>>8;
+ u->appendserver[u->appendserverbytes++]=u->loc[a].y;
+ u->appendserver[u->appendserverbytes++]=u->loc[a].y>>8;
+ u->appendserver[u->appendserverbytes++]=u->loc[a].z;
+ u->appendserver[u->appendserverbytes++]=u->loc[a].z>>8;
+ }
+ else // new mark
+ {
+ if(strlen(s)>1)
+ {
+ q_setlocname(u,u->locations,s);
+ u->loc[u->locations].x = u->pos[0];
+ u->loc[u->locations].y = u->pos[1];
+ u->loc[u->locations].z = u->pos[2];
+ u->locations++;
+ }
+
+ q_printf(u,"%s marked\n",s);
+ q_savelocs(u);
+
+ u->appendserver[u->appendserverbytes++]=0x17;
+ u->appendserver[u->appendserverbytes++]=0x04;
+ u->appendserver[u->appendserverbytes++]=u->pos[0];
+ u->appendserver[u->appendserverbytes++]=u->pos[0]>>8;
+ u->appendserver[u->appendserverbytes++]=u->pos[1];
+ u->appendserver[u->appendserverbytes++]=u->pos[1]>>8;
+ u->appendserver[u->appendserverbytes++]=u->pos[2];
+ u->appendserver[u->appendserverbytes++]=u->pos[2]>>8;
+ }
+ }
+#ifdef TIMERS
+ else if(!memicmp(s,".cam",4))
+ { // camp these
+ nextparam(&s);
+ if(*s) u->campthese=0;
+
+ if(u->notimers)
+ {
+ sprintf(buf,"Timers disabled with .notimers.\n");
+ }
+ else if(u->fpd&FPD_TIMERS)
+ {
+ sprintf(buf,"Timers disabled.\n");
+ }
+ else
+ {
+ strcpy(buf,"Timing: ");
+ modifycampflags(s,&u->campthese,buf+strlen(buf),CAMP_P|CAMP_Q|CAMP_R,CAMP_DEF);
+ strcat(buf,"\n");
+ }
+
+ q_print(u,buf);
+ }
+ else if(!memicmp(s,".rem",4))
+ { // remind
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->remindcamp=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ u->remindcamp=atoi(p);
+ if(u->remindcamp<0) u->remindcamp=0;
+ break;
+ }
+
+ if(u->remindcamp) sprintf(buf,"Remind: %i seconds before item\n",u->remindcamp);
+ else strcpy(buf,"Remind: disabled\n");
+ q_print(u,buf);
+ }
+ else if(!memicmp(s,".gue",4))
+ { // guess
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->secondcamp=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ u->secondcamp=atoi(p);
+ if(u->secondcamp<=0) u->secondcamp=0;
+ if(u->secondcamp>1) u->secondcamp=1;
+ break;
+ }
+
+ if(u->secondcamp) strcpy(buf,"Guess: estimate if not known\n");
+ else strcpy(buf,"Guess: disabled\n");
+ q_print(u,buf);
+ }
+ else if(!memicmp(s,".notim",6))
+ {
+ int check=1;
+ nextparam(&s);
+ if(*s<=32) check=0;
+ if(*s==0x7f)
+ {
+ check=0;
+ s++;
+ }
+
+ if(check && u->amconnected && *s!='1')
+ {
+ q_print(u,"Notimers mode can only be cleared\n"
+ "when not connected to a server.\n");
+ }
+ else if(check && u->linkedclient)
+ {
+ q_print(u,"Notimers mode has to be set in the\n"
+ "first proxy. Use .notimers instead\n"
+ "of proxy:notimers.\n");
+ }
+ else
+ {
+ char buf[64];
+
+ if(*s=='1')
+ {
+ u->campthese=0;
+ u->notimers=1;
+ u->fpd|=FPD_TIMERS;
+
+ sprintf(buf,"%s notimers",version);
+ cl_setinfo(u,"Cheapo",buf);
+ if(u->amconnected)
+ {
+ sprintf(buf,"Cheapo \"%s notimers\"",version);
+ sendsetinfo(u,buf);
+ }
+
+ if(u->linkedserver)
+ {
+ sprintf(buf,"say .notimers \x7f%i",u->notimers);
+ q_servercmd(u,buf);
+ }
+ }
+ else if(*s=='0')
+ {
+ u->campthese=0;
+ u->notimers=0;
+ u->fpd=0;
+
+ sprintf(buf,"%s",version);
+ cl_setinfo(u,"Cheapo",buf);
+
+ if(u->linkedserver)
+ {
+ sprintf(buf,"say .notimers \x7f%i",u->notimers);
+ q_servercmd(u,buf);
+ }
+ }
+
+ if(u->notimers)
+ {
+ q_print(u,"Notimers: powerup timers disabled\n");
+ }
+ else
+ {
+ q_print(u,"Notimers: powerup timers enabled\n");
+ }
+ }
+ }
+#else
+ else if(!memicmp(s,".rem",4))
+ {
+ //ignore
+ }
+ else if(!memicmp(s,".gue",4))
+ {
+ //ignore
+ }
+ else if(!memicmp(s,".cam",4))
+ {
+ //ignore
+ }
+ else if(!memicmp(s,".not",4))
+ {
+ //ignore
+ }
+#endif
+ else if(!memicmp(s,".poi",4))
+ { // camp these
+ nextparam(&s);
+
+ {
+ if(*s) u->pointtell=0;
+ strcpy(buf,"%t pointing: ");
+ modifycampflags(s,&u->pointtell,buf+strlen(buf),CAMP_LAST-1,CAMP_DEF);
+ strcat(buf,"\n");
+ }
+ *u->msgline=0;
+
+ q_print(u,buf);
+ }
+ else if(!memicmp(s,".too",4))
+ { // took messages
+ nextparam(&s);
+
+ {
+ if(*s) u->autoitookitems=0;
+ strcpy(buf,"auto f_took items: ");
+ modifycampflags(s,&u->autoitookitems,buf+strlen(buf),CAMP_LAST-1,CAMP_DEFTOOK);
+ strcat(buf,"\n");
+ }
+ *u->msgline=0;
+
+ q_print(u,buf);
+ if(!u->autoitook)
+ {
+ q_print(u,"f_took disabled (see .report)\n");
+ }
+ }
+/*
+ else if(!memicmp(s,".spo",4))
+ { // camp these
+ nextparam(&s);
+ if(*s) u->pointshow=0;
+
+ {
+ strcpy(buf,"screen pointing: ");
+ modifycampflags(s,&u->pointshow,buf+strlen(buf),CAMP_LAST-1,CAMP_DEF);
+ strcat(buf,"\n");
+ }
+ *u->msgline=0;
+
+ q_print(u,buf);
+ }
+*/
+ else if(!memicmp(s,".fmo",4))
+ { // fmodified
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->fmodified=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ u->fmodified=atoi(p);
+ break;
+ }
+
+ if(u->fmodified) sprintf(buf,"F_modified: enabled\n");
+ else strcpy(buf,"F_modified: disabled\n");
+ q_print(u,buf);
+ if(u->fmodified)
+ {
+ if(!*quakepath)
+ {
+ q_print(u,"QWCL has not been started from\n"
+ "Cheapo; f_modified will not work.\n");
+ }
+ else if(quakemodified)
+ {
+ q_print(u,"QWCL executable has been modified;\n"
+ "f_modified will not work.\n");
+ }
+ else if(!c_islocalconnect(&u->toclientaddr))
+ {
+ q_print(u,"QWCL client is not local;\n"
+ "f_modified will not work.\n");
+ }
+ }
+ }
+ else if(!memicmp(s,".mov",4))
+ { // move
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->moveoverlay=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ u->moveoverlay=atoi(p);
+ if(u->moveoverlay<=0) u->moveoverlay=0;
+ if(u->moveoverlay>9) u->moveoverlay=9;
+ break;
+ }
+
+ if(u->secondcamp) sprintf(buf,"Move texts: %i lines down\n",u->moveoverlay);
+ else strcpy(buf,"Move texts: disabled\n");
+ q_print(u,buf);
+ }
+ else if(!memicmp(s,".flo",4))
+ { // flood
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->floodprot=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ u->floodprot=atoi(p);
+ if(u->floodprot<=0) u->floodprot=0;
+ if(u->floodprot>1) u->floodprot=1;
+ break;
+ }
+
+ if(u->floodprot) strcpy(buf,"Flood protect protect: enabled\n");
+ else strcpy(buf,"Flood protect protect: disabled\n");
+ q_print(u,buf);
+ }
+#ifdef RLPACK
+ else if(!memicmp(s,".pac",4))
+ { // pack
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->classifypacks=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ u->classifypacks=atoi(p);
+ if(u->classifypacks<=0) u->classifypacks=0;
+ if(u->classifypacks>1) u->classifypacks=1;
+ break;
+ }
+
+ if(u->classifypacks) strcpy(buf,"Packs: detect rl\n");
+ else strcpy(buf,"Packs: normal\n");
+ q_print(u,buf);
+ }
+#endif
+ else if(!memicmp(s,".cpp",4))
+ { // cpps
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->cpps=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ u->cpps=atoi(p);
+ if(u->cpps<=0) u->cpps=0;
+ if(u->cpps>100) u->cpps=100;
+
+ break;
+ }
+
+ u->time_nextcpps=0;
+ if(u->cpps) u->time_cppsdelta=1000/u->cpps;
+ else u->time_cppsdelta=0;
+ u->cppsdrop=1;
+ u->cppspass=1;
+
+ if(u->cpps) sprintf(buf,"Client to server: %i packets/sec\n",u->cpps);
+ else strcpy(buf,"Client to server: no limit\n");
+ q_print(u,buf);
+ }
+#ifdef COMPRESS
+ else if(!memicmp(s,".com",4))
+ { // compress
+ uchar *p;
+ int compwas=u->compress_client;
+
+ nextparam(&s);
+ if(*s)
+ {
+ u->compress_client=0;
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ u->compress_client=atoi(p);
+ break;
+ }
+
+ if(u->compress_client && !u->linkedserver)
+ {
+ u->compress_client=0;
+ q_print(u,"Cannot compress: not connected\n"
+ "to a compatible serverside proxy.\n");
+ }
+
+ if(compwas!=u->compress_client)
+ {
+ if(u->compress_client) sprintf(buf,"Client compress: %s\n",u->proxyhostname);
+ else sprintf(buf,"Client compress: disabled\n",u->proxyhostname);
+ q_print(u,buf);
+ }
+
+ if(u->compress_client) q_print(u,"Compression: enabled\n");
+ else q_print(u,"Compression: disabled\n");
+ }
+ else
+ {
+ int i,c1=0,c2=0,s1=0,s2=0,cr,sr;
+ for(i=0;i<16;i++)
+ {
+ c1+=u->packetclientinsize[i];
+ c2+=u->packetclientoutsize[i];
+ s1+=u->packetserverinsize[i];
+ s2+=u->packetserveroutsize[i];
+ }
+
+ if(!c1) cr=0;
+ else cr=100*c2/c1;
+ c1>>=4;
+ c2>>=4;
+
+ if(!s1) sr=0;
+ else sr=100*s2/s1;
+ s1>>=4;
+ s2>>=4;
+
+ if(u->compress_client) q_print(u,"Compression: enabled\n");
+ else q_print(u,"Compression: disabled\n");
+
+ /*
+ q_print(u,"Delta counts:\n");
+ for(i=0;i<8;i++)
+ {
+ sprintf(buf,"e%i: %i\n",i,expcount[i]);
+ q_print(u,buf);
+ }
+ */
+
+ sprintf(buf,"Client ratio: %i%% (%i to %i)\n",cr,c1,c2);
+ q_print(u,buf);
+ sprintf(buf,"Server ratio: %i%% (%i to %i)\n",sr,s1,s2);
+ q_print(u,buf);
+ }
+ }
+#endif
+ else if(!memicmp(s,".rep",4))
+ { // autoreport
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->autofreport=0;
+ u->autoihave=0;
+ u->autoitook=0;
+ u->autoqpicked=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ if(!memicmp(p,"rep",3))
+ {
+ u->autofreport=1;
+ }
+#ifdef POWERDROP
+ if(!memicmp(p,"power2",6))
+ {
+ u->autoihave|=2;
+ }
+#endif
+ if(!memicmp(p,"pow",3))
+ {
+ u->autoihave|=1;
+ }
+ if(!memicmp(p,"too",3))
+ {
+ u->autoitook=1;
+ }
+#ifdef TIMERS
+ if(!memicmp(p,"syn",3))
+ {
+ if(!u->autoqpicked) u->autoqpicked=1;
+ }
+ if(!memicmp(p,"sho",3))
+ {
+ u->autoqpicked=2;
+ }
+#endif
+ }
+
+ {
+ int x=0;
+ sprintf(buf,"Reporting: ");
+ if(u->autofreport) { x=1; strcat(buf,"reply_report "); }
+ if(u->autoihave&2) { x=1; strcat(buf,"powerup_2 "); }
+ if(u->autoihave&1) { x=1; strcat(buf,"powerup_have "); }
+ if(u->autoitook) { x=1; strcat(buf,"took_item "); }
+ if(u->autoqpicked==2) { x=1; strcat(buf,"show&sync_powerup "); }
+ else if(u->autoqpicked) { x=1; strcat(buf,"sync_powerup "); }
+ if(!x) strcat(buf,"nothing");
+ strcat(buf,"\n");
+ q_print(u,buf);
+ }
+ }
+#if 0
+ else if(!memicmp(s,".tim",4))
+ { // timer settings
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->secondcamp=0;
+ u->remindcamp=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ if(!memicmp(p,"gue",3))
+ { // guess
+ u->secondcamp=1;
+ }
+ else if(!memicmp(p,"syn",3))
+ { // sync
+ u->internalsync=1;
+ }
+ else if(!memicmp(p,"rem",3))
+ { // remind
+ p=nextparam(&s);
+ u->remindcamp=atoi(p);
+ if(u->remindcamp<=0) u->remindcamp=10;
+ }
+ }
+
+ {
+ int x=0;
+ sprintf(buf,"Active timer options:\n");
+ if(u->remindcamp>0) { x=1; sprintf(buf+strlen(buf),"- remind at %i seconds\n",u->remindcamp); }
+ if(u->secondcamp) { x=1; strcat(buf,"- guess collected at timeout\n"); }
+ if(u->internalsync) { x=1; strcat(buf,"- syncronize with players in same proxy\n"); }
+ if(!x) strcat(buf,"none\n");
+ q_print(u,buf);
+ }
+ }
+#endif
+#ifdef SPEED
+ else if(!memicmp(s,".sho",4))
+ { // show
+ int a=0;
+ char *p;
+
+ nextparam(&s);
+ if(*s>32) a=atoi(s);
+
+ u->showspeed=0;
+ u->showtime=0;
+ u->showdist=0;
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ if(*p=='s')
+ {
+ u->showspeed=1;
+ }
+ if(*p=='t')
+ {
+ u->showtime=1;
+ }
+ if(*p=='h')
+ {
+ u->showhud=1;
+ }
+ if(*p=='d') { u->showdist=1; } }
+ u->showtimebase=u->time; u->showdistbase[0]=u->pos[0];
+ u->showdistbase[1]=u->pos[1];
+ u->showdistbase[2]=u->pos[2];
+
+ if(!u->showspeed && !u->showtime && !u->showdist && !u->showhud)
+ {
+ q_print(u,"Show: nothing\n(options: speed dist time hug)\n");
+ }
+ else
+ {
+ strcpy(buf,"Show: ");
+ if(u->showspeed) strcat(buf,"speed ");
+ if(u->showtime) strcat(buf,"time ");
+ if(u->showdist) strcat(buf,"distance ");
+ if(u->showhud) strcat(buf,"hud ");
+ strcat(buf,"\n");
+ q_print(u,buf);
+ }
+ u->overlayactive=0;
+ }
+#endif
+#ifdef PROMODE
+ else if(!memicmp(s,".deb",4))
+ {
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ *u->msgline=0;
+ u->showtime=0;
+ u->showspeed=0;
+ u->hideaxemen=0;
+ }
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ if(!memicmp(p,"tim",3))
+ { // time
+ u->showtime=1;
+ q_print(u,"Debug: show time\n");
+ }
+ else if(!memicmp(p,"spe",3))
+ { // speed
+ u->showspeed=1;
+ q_print(u,"Debug: show time\n");
+ }
+ else if(!memicmp(p,"dip",3))
+ { // dynamic ip test
+ int i;
+ for(i=0;i<MAXUSERS;i++)
+ {
+ memset(&user[i].toclientaddr,0,sizeof(SOCKADDR_IN));
+ }
+ }
+ else if(!memicmp(p,"mov",3))
+ { // speed
+ u->moveoverlay=1;
+ q_print(u,"Debug: move overlay\n");
+ }
+ else if(!memicmp(p,"com",3))
+ { // compress
+ int a;
+ p=nextparam(&s);
+ a=atoi(p);
+ u->compress_client=a&1;
+ u->compress_server=(a&2)>>1;
+
+ q_printf(u,"Debug: compress cli %i ser %i\n",
+ u->compress_client,u->compress_server);
+ }
+ else if(!memicmp(p,"cte",3))
+ { // compress test
+ int a;
+
+ count_load(u);
+ count_save(u);
+
+ p=nextparam(&s);
+ a=atoi(p);
+ u->compress_test=a;
+
+ q_printf(u,"Debug: compress test %i\n",
+ u->compress_test);
+ }
+ else if(!memicmp(p,"ser",3))
+ {
+ sprintf(buf,"Debug: servercmd %s sent\n",s);
+ q_print(u,buf);
+ q_servercmd(u,s);
+ }
+ else if(!memicmp(p,"axe",3))
+ { // speed
+ u->hideaxemen=1;
+ q_print(u,"Debug: hide axe men (rocket arena)\n");
+ }
+ else if(!memicmp(p,"fpd",3))
+ { // fpd clear
+ u->fpd=0;
+ u->fpdcopy=0;
+ q_print(u,"Debug: fpd cleared\n");
+ }
+ else if(!memicmp(p,"beg",3))
+ { // restart
+ q_print(u,"Debug: restarting with begin\n");
+ q_printf(u,"begin %i\n",u->lastbegin);
+ q_servercmd(u,tmpbuf);
+ }
+ else if(!memicmp(p,"pin",3))
+ { // ping fake
+ p=nextparam(&s);
+ u->fakeping=atoi(p);
+ q_printf(u,"Debug: ping test %i\n",u->fakeping);
+ }
+ else if(!memicmp(p,"fv1",3))
+ { // yes f version
+ if(u->state==STATE_HERE)
+ {
+ char ver[16];
+ sprintf(ver,"%s",version);
+ cl_setinfo(u,"Cheapo",ver);
+ }
+ else
+ {
+ sprintf(tmpbuf,"setinfo Cheapo \"%s\"",version);
+ q_stufftext(u,tmpbuf);
+ }
+ u->nofversion=0;
+ q_print(u,"Debug: f_version enabled\n");
+ }
+ else if(!memicmp(p,"fv",2)) // also fv0
+ { // no f version
+ if(u->state==STATE_HERE)
+ {
+ cl_setinfo(u,"Cheapo","");
+ }
+ else
+ {
+ sprintf(tmpbuf,"setinfo Cheapo \"\"");
+ q_stufftext(u,tmpbuf);
+ }
+ u->nofversion=1;
+ q_print(u,"Debug: f_version disabled\n");
+ }
+ else if(!memicmp(p,"poi",3))
+ { // point
+ findpoint(u,u->pos[0],u->pos[1],u->pos[2],u->ang[0],u->ang[1],u->ang[2],-1,0,NULL);
+ }
+ }
+ }
+#endif
+ else if(!memicmp(s,".sou",4))
+ {
+ uchar *p;
+ nextparam(&s);
+ if(u->fpd&FPD_SKIN)
+ {
+ *s=0;
+ q_print(u,"Soundtrigger disabled.\n");
+ }
+ if(*s)
+ {
+ u->soundtrigger=0;
+ }
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ if((*p>='A' && *p<='Z') || (*p>='a' && *p<='z'))
+ { // do not allow letters
+ ;
+ }
+ else
+ { // best
+ u->soundtrigger=*p;
+ }
+ }
+
+ {
+ if(u->soundtrigger) sprintf(buf,"Soundtrigger: %c",u->soundtrigger);
+ else strcpy(buf,"Soundtrigger: disabled\n");
+ strcat(buf,"\n");
+
+ q_print(u,buf);
+ }
+ }
+ else if(!memicmp(s,".wea",4))
+ {
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ u->bestweapon=0;
+ }
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ if(!memicmp(p,"bes",3))
+ { // best
+ u->bestweapon=1;
+ }
+ else if(p[strlen(p)-1]==':')
+ { // define/undefine own
+ int i,seq,imp,imp2;
+
+ imp=atoi(p);
+ if(imp<9)
+ {
+ q_print(u,"Cannot redefine sequences\n"
+ "for impulses 1..8\n");
+ break;
+ }
+
+ // find if already defined
+ for(seq=0;seq<SEQUENCES;seq++)
+ {
+ if(u->impulseorder[seq][0]==imp) break;
+ }
+ if(seq==SEQUENCES)
+ {
+ for(seq=0;seq<SEQUENCES;seq++)
+ {
+ if(u->impulseorder[seq][0]==0)
+ {
+ u->impulseorder[seq][0]=imp;
+ break;
+ }
+ }
+ }
+ if(seq==SEQUENCES)
+ {
+ q_printf(u,"All %i sequences are defined.\n"
+ "You have to delete an existing\n"
+ "one with '.weapon #:' where #\n"
+ "is the impulse to delete\n",SEQUENCES);
+ break;
+ }
+
+ for(i=1;i<15;i++) u->impulseorder[seq][i]=0;
+
+ for(i=1;i<10;i++) // limit to 10 impulses
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+ imp2=atoi(p);
+ if(!p) break;
+
+ u->impulseorder[seq][i]=imp2;
+ }
+ if(i==1) u->impulseorder[seq][0]=0;
+ }
+ else if(atoi(p)==1)
+ { // enable
+ u->bestweapon=1;
+ }
+
+ break;
+ }
+
+ {
+ int seq,i,def=0;
+ strcpy(buf,"Weapon: ");
+
+ if(u->bestweapon) strcat(buf,"select best with ammo");
+ else strcat(buf,"normal impulses");
+ strcat(buf,"\n");
+ q_print(u,buf);
+
+ for(seq=0;seq<SEQUENCES;seq++)
+ {
+ if(u->impulseorder[seq][0])
+ {
+ q_printf(u,"Sequence %i: ",u->impulseorder[seq][0]);
+ for(i=1;i<14;i++)
+ {
+ if(!u->impulseorder[seq][i]) break;
+ q_printf(u,"%i ",u->impulseorder[seq][i]);
+ }
+ q_print(u,"\n");
+ def=1;
+ }
+ }
+ }
+ }
+ else if(!memicmp(s,".ski",4))
+ {
+ uchar *p;
+ nextparam(&s);
+ if(u->fpd&FPD_SKIN)
+ {
+ q_print(u,"Skin override disabled.\n");
+ u->skinoverride=0;
+ }
+ else if(u->version<22)
+ {
+ q_print(u,"Skin command requires QW 2.21 or newer\n");
+ u->skinoverride=0;
+ }
+ else if(u->fortress)
+ {
+ q_print(u,"Skin override disabled in TF.\n");
+ u->skinoverride=0;
+ }
+ else
+ {
+ if(*s)
+ {
+ u->skinoverride=1;
+
+ p=nextparam(&s);
+ strncpy(u->skinname,p,15);
+
+ p=nextparam(&s);
+ if(*p>=32) strncpy(u->skinname2,p,15);
+ else strcpy(u->skinname2,"base");
+
+ sprintf(buf,"Skin override: us \'%s\' them \'%s\'\n",u->skinname,u->skinname2);
+ q_print(u,buf);
+
+ u->skinoverridepos=0;
+ }
+ else
+ {
+ if(!(u->fpd&FPD_SKIN)) q_print(u,"Skin override: off\n");
+
+ u->skinoverride=-1; // clear
+ u->skinoverridepos=0;
+ }
+
+ //u->time_reskinall=u->time;
+ if(u->time_reskinall==0) u->time_reskinall=u->time;
+ }
+ }
+ else if(!memicmp(s,".col",4))
+ {
+ uchar *p;
+ nextparam(&s);
+ if(u->fpd&FPD_COLOR)
+ {
+ q_print(u,"Color override disabled.\n");
+ u->coloroverride=0;
+ }
+ else if(u->version<22)
+ {
+ q_print(u,"Color command requires QW 2.21 or newer\n");
+ u->coloroverride=0;
+ }
+ else if(u->fortress)
+ {
+ q_print(u,"Color override disabled in TF.\n");
+ u->coloroverride=0;
+ }
+ else
+ {
+ if(*s)
+ {
+ char buf[64];
+
+ u->coloroverride=1;
+
+ p=nextparam(&s);
+ u->color=text2color(p);
+
+ p=nextparam(&s);
+ u->color2=text2color(p);
+
+ if(u->color2==-2) u->color=-1;
+
+ strcpy(buf,"Color override: us ");
+ strcat(buf,color2text(u->color));
+ strcat(buf,"them ");
+ strcat(buf,color2text(u->color2));
+ strcat(buf,"\n");
+ q_print(u,buf);
+
+ u->skinoverridepos=0;
+ }
+ else
+ {
+ if(!(u->fpd&FPD_COLOR)) q_print(u,"Color override: off\n");
+
+ u->coloroverride=-1; // clear
+ u->skinoverridepos=0;
+ }
+
+ //u->time_reskinall=u->time;
+ if(u->time_reskinall==0) u->time_reskinall=u->time;
+ }
+ }
+ else if(!memicmp(s,".fps",4))
+ { // fps enhancement
+ {
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ if(*s)
+ {
+ u->fps_explosion=0;
+ u->fps_powerup=0;
+ u->fps_muzzle=0;
+ u->fps_gib=0;
+ u->fps_rocket=0;
+ u->fps_setinfo=0;
+ u->fps_damage=0;
+ u->fps_nails=0;
+ }
+
+ {
+ if(*s>='0' && *s<='9') u->fps_explosion=atoi(s);
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ if(*s>='0' && *s<='9') u->fps_powerup=atoi(s);
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ if(*s>='0' && *s<='9') u->fps_muzzle=atoi(s);
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ if(*s>='0' && *s<='9') u->fps_gib=atoi(s);
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ if(*s>='0' && *s<='9') u->fps_rocket=atoi(s);
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ if(*s>='0' && *s<='9') u->fps_setinfo=atoi(s);
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ if(*s>='0' && *s<='9') u->fps_nails=atoi(s);
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ if(*s>='0' && *s<='9') u->fps_damage=atoi(s);
+ }
+ }
+ sprintf(buf,"FPS enhancement: %i %i %i %i %i %i %i %i\n",
+ u->fps_explosion,u->fps_powerup,u->fps_muzzle,
+ u->fps_gib,u->fps_rocket,
+ u->fps_setinfo,u->fps_nails,u->fps_damage);
+ q_print(u,buf);
+ switch(u->fps_explosion) // explosion
+ {
+ default:
+ u->fps_explosion=0;
+ case 0: // normal
+ q_print(u,"- Explosions: normal\n");
+ break;
+ case 1: // tarbaby
+ q_print(u,"- Explosions: tarbaby\n");
+ break;
+ case 2: // teleport
+ q_print(u,"- Explosions: teleport\n");
+ break;
+ case 3: // blood
+ q_print(u,"- Explosions: blood\n");
+ break;
+ case 4: // nothing
+ q_print(u,"- Explosions: nothing\n");
+ break;
+ case 5: // shotgun
+ q_print(u,"- Explosions: shotgun\n");
+ break;
+ }
+ switch(u->fps_powerup) // powerupp glow
+ {
+ default:
+ u->fps_powerup=0;
+ case 0: // normal
+ q_print(u,"- Powerup glow: normal\n");
+ break;
+ case 1: // oma pois
+ q_print(u,"- Powerup glow: own off\n");
+ break;
+ case 2: // kaikki pois
+ q_print(u,"- Powerup glow: all off\n");
+ break;
+ }
+ switch(u->fps_muzzle) // muzzle
+ {
+ default:
+ u->fps_muzzle=0;
+ case 0: // normal
+ q_print(u,"- Muzzle flash: normal\n");
+ break;
+ case 1: // oma pois
+ q_print(u,"- Muzzle flash: own off\n");
+ break;
+ case 2: // kaikki pois
+ q_print(u,"- Muzzle flash: all off\n");
+ break;
+ }
+ switch(u->fps_gib) // gib filter
+ {
+ default:
+ u->fps_gib=0;
+ case 0: // normal (off)
+ q_print(u,"- Gibs/bodies: normal\n");
+ break;
+ case 1: // on
+ q_print(u,"- Gibs/bodies: removed\n");
+ break;
+ case 2: // on
+ q_print(u,"- Gibs/bodies: only bodies\n");
+ break;
+ }
+ switch(u->fps_rocket) // rocket cchange
+ {
+ default:
+ u->fps_muzzle=0;
+ case 0: // normal
+ q_print(u,"- Rockets: normal\n");
+ break;
+ case 1: // no glow
+ q_print(u,"- Rockets: grenade (no glow)\n");
+ break;
+ }
+ switch(u->fps_setinfo) // setinfoo filter
+ {
+ default:
+ u->fps_setinfo=0;
+ case 0: // normal
+ q_print(u,"- Setinfo: not supported\n");
+ break;
+ }
+ switch(u->fps_nails) // nails fillter
+ {
+ default:
+ u->fps_nails=0;
+ case 0:
+ q_print(u,"- Nails: normal\n");
+ break;
+ case 1:
+ q_print(u,"- Nails: normal\n");
+ break;
+ case 2:
+ q_print(u,"- Nails: 1/2\n");
+ break;
+ case 3:
+ q_print(u,"- Nails: 2/3\n");
+ break;
+ case 4:
+ q_print(u,"- Nails: 3/4\n");
+ break;
+ }
+ switch(u->fps_damage) // damage ffilter
+ {
+ default:
+ u->fps_damage=0;
+ case 0: // normal
+ q_print(u,"- Damage flash: normal\n");
+ break;
+ case 1: // normal
+ q_print(u,"- Damage flash: filtered out\n");
+ break;
+ }
+ // reset entity table
+ memset(u->entitygib,0,MAXENTITIES);
+ }
+ else if(!memicmp(s,".lin",4))
+ { // line
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+ u->linesright=36;
+ if(!memicmp(s,"640",3) || !memicmp(s,"gl",2))
+ {
+ u->linesdown=45;
+ }
+ else if(!memicmp(s,"512",3))
+ {
+ u->linesdown=33;
+ }
+ else if(!memicmp(s,"320",3) || !memicmp(s,"vg",2))
+ {
+ u->linesdown=11;
+ }
+ else if(*s>='0' && *s<'9')
+ {
+ u->linesdown=atoi(s);
+ }
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+
+ if(!memicmp(s,"cen",3)) u->linecenter=1;
+ else u->linecenter=0;
+
+ if(u->linesdown>50) u->linesdown=50;
+ if(u->linesright>50) u->linesright=50;
+
+ if(u->linecenter) sprintf(buf,"Screen lines: %i, center timers\n",u->linesdown);
+ else sprintf(buf,"Screen lines: %i\n",u->linesdown);
+ q_print(u,buf);
+ }
+ else if(!memicmp(s,".ite",4))
+ { // items
+ int i,c;
+ int intlist;
+
+ nextparam(&s);
+ intlist=atoi(s);
+#ifndef PROMODE
+ intlist=0;
+#endif
+ if(intlist<=0) intlist=0;
+
+ if(intlist==2)
+ {
+ q_print(u,"[visible item]\n");
+ for(i=0;i<MAXENTITIES;i++) if(u->entityvisible[i])
+ {
+ sprintf(buf,"%-8s (%i,%i,%i)\n",
+ u->entityname[i],
+ u->entitypos[i][0],
+ u->entitypos[i][1],
+ u->entitypos[i][2]);
+ q_print(u,buf);
+ }
+ }
+ else
+ {
+ q_print(u,"[item] [nearest mark]\n");
+ for(c=CAMP_LASTAUTOMARK;c>=1;c>>=1)
+ {
+ if(c==CAMP_LASTPOWER ||
+ c==CAMP_LASTARMOR ||
+ c==CAMP_LAST)
+ {
+ uchar tmp[36];
+ memset(tmp,0x9e,31);
+ tmp[0]=0x9d;
+ tmp[30]=0x9f;
+ tmp[31]='\n';
+ tmp[32]=0;
+ q_print(u,tmp);
+ }
+ for(i=0;i<u->camps;i++)
+ {
+ Camp *ca;
+ uchar *text;
+ ca=&u->camp[i];
+ if(ca->category&c)
+ {
+ char name[16];
+
+ strcpy(name,ca->genericname);
+ name[strlen(name)+1]=0;
+ name[strlen(name)]=ca->name[strlen(ca->name)-1];
+
+ text=findnear(u,ca->pos[0],ca->pos[1],ca->pos[2],NULL);
+ if(!intlist) sprintf(buf,"%-8s near %s\n",name,text);
+ else sprintf(buf,"%-8s %s (%i,%i,%i)\n",
+ name,text,ca->pos[0],ca->pos[1],ca->pos[2]);
+ q_print(u,buf);
+ }
+ }
+ }
+ }
+
+ if(intlist)
+ {
+ sprintf(buf,"you (%i,%i,%i) -> (%i,%i,%i)\n",
+ u->pos[0],u->pos[1],u->pos[2],
+ u->ang[0],u->ang[1],u->ang[2]);
+ q_print(u,buf);
+ }
+ }
+ else if(!memicmp(s,".sta",4))
+ {
+#if !defined(__APPLE__) && !defined(MACOSX)
+ int firstround=1;
+ int pingcnt=0;
+#endif /* !__APPLE__ && !MACOSX */
+ int i,n,loss=0,ping=0,avgs=0,avgc=0,ratio;
+ uchar *cmd,*p;
+
+ if(u->state==STATE_HERE)
+ {
+ q_print(u,"not connected\n");
+ return;
+ }
+
+ { // init stuff
+ for(n=0;n<STATNUM/2;n++)
+ {
+ i=(u->lastpacketrecv+4+n)&(STATNUM-1);
+ if(!u->packetrecvtime[i]) loss++;
+ }
+ loss=200*loss/STATNUM;
+
+ for(n=0;n<16;n++)
+ {
+ avgs+=u->packetserversize[n];
+ avgc+=u->packetclientsize[n];
+ }
+ avgs=(avgs+8)/16;
+ avgc=(avgc+8)/16;
+
+ ping=u->server_ping;
+
+ if(u->cpps) ratio=100*u->cppsdrop/(u->cppsdrop+u->cppspass);
+ else ratio=0;
+
+ }
+
+ cmd=s;
+ p=nextparam(&s);
+ if(*s<=32) s="pro ser net";
+ else s=cmd;
+
+ for(;;)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+
+ if(!memicmp(p,"all",3)) p=NULL;
+
+ if(!p || !memicmp(p,"pro",3))
+ { // proxies
+ if(u->linkedclient) q_print(u,"Client side proxy present.\n");
+ if(u->linkedserver) q_print(u,"Server side proxy present.\n");
+ }
+
+ if(!p || !memicmp(p,"ser",3))
+ { // default stats
+ q_printf(u,"Serverside statistics:\n"
+ "data to server : %i bytes/sec\n"
+ "data to client : %i bytes/sec\n"
+ ".cpps dropping : %i%%\n"
+ "packet loss : %i%%\n"
+ "average ping : %i ms\n",
+ u->bytestoserversec,
+ u->bytestoclientsec,
+ ratio,loss,ping);
+ }
+
+ if(!p || !memicmp(p,"ext",3))
+ { // extended stats
+ q_printf(u,
+ "Extended statistics:\n"
+ "out of order-pkt: %i\n"
+ "data to server : %i KB\n"
+ "data to client : %i KB\n"
+ "aver server-pkt : %i bytes\n"
+ "aver client-pkt : %i bytes\n"
+ "peak server-pkt : %i bytes\n"
+ "peak client-pkt : %i bytes\n",
+ u->outoforder,
+ u->bytestoserver/1024,
+ u->bytestoclient/1024,
+ avgs,
+ avgc,
+ u->bytestoserversecmax,
+ u->bytestoclientsecmax);
+ }
+
+ if(!p || !memicmp(p,"pin",3))
+ { // packet pings
+ strcpy(buf,"Pings for last 8 packets:\n");
+ for(n=0;n<8;n++)
+ {
+ i=(STATNUM+u->lastpacketrecv-1-n)&(STATNUM-1);
+ if(!u->packetrecvtime[i]) sprintf(buf+strlen(buf)," L ");
+ else sprintf(buf+strlen(buf),"%3i ",u->packetrecvtime[i]-u->packetsendtime[i]);
+ }
+ strcat(buf,"\n");
+ q_print(u,buf);
+ }
+
+ if(!p || !memicmp(p,"net",3))
+ { // netgraph
+ strcpy(buf,"Netgraph for last 30 packets:\n");
+ q_print(u,buf);
+
+ for(n=0;n<30;n++)
+ {
+ i=(STATNUM+u->lastpacketrecv-1-n)&(STATNUM-1);
+ if(!u->packetrecvtime[i]) buf[n]='L';
+ else if(u->packetchoke[i]) buf[n]='=';
+ else buf[n]='-';
+ }
+ buf[n]='\n';
+ buf[n+1]=0;
+ q_print(u,buf);
+ }
+
+ if(p && !memicmp(p,"res",3))
+ { // reset
+ u->bytestoserver=0;
+ u->bytestoclient=0;
+ u->bytestoserversecmax=0;
+ u->bytestoclientsecmax=0;
+ u->outoforder=0;
+ }
+ }
+ }
+ else if(!memicmp(s,".dmm",4))
+ {
+ nextparam(&s);
+ if(*s<=32) ;
+ else u->forcedmm=atoi(s);
+ if(u->forcedmm<0) u->forcedmm=0;
+ if(u->forcedmm>3) u->forcedmm=3;
+
+ if(u->forcedmm) q_printf(u,"Dm-mode: assume %i\n",u->forcedmm);
+ else q_printf(u,"Dm-mode: autodetect\n");
+
+ dmmchange(u,u->originaldmm);
+ }
+ else if(!memicmp(s,".qui",4))
+ {
+ nextparam(&s);
+ if(*s<=32)
+ {
+ u->quiet=10; // for 10 packets
+ }
+ else
+ {
+ if(atoi(s)) u->quiet=0x7fffffff;
+ else u->quiet=0;
+ }
+ }
+ else if(!memicmp(s,".who",4))
+ {
+ int i;
+ int mode;
+
+ nextparam(&s);
+ if(*s=='f') mode=2;
+ else if(*s=='p') mode=1;
+ else mode=0;
+
+ if(mode==2) sprintf(buf,"[name] [fps]\n");
+ else if(mode==1) sprintf(buf,"[name] [proxy]\n");
+ else sprintf(buf,"[name] [team] [skin]\n");
+ q_print(u,buf);
+
+ for(i=0;i<34;i++) if(u->playeractive[i])
+ {
+ if(mode==2)
+ {
+ if(u->playernear[i])
+ {
+ sprintf(buf,"%-17s %3i\n",
+ u->playername[i],u->playerfps[i]);
+ }
+ else
+ {
+ if(u->playerfps[i])
+ {
+ sprintf(buf,"%-17s (%3i)\n",
+ u->playername[i],u->playerfps[i]);
+ }
+ else
+ {
+ sprintf(buf,"%-17s ( - )\n",
+ u->playername[i]);
+ }
+ }
+ }
+ else if(mode==1)
+ {
+ sprintf(buf,"%-17s %s\n",u->playername[i],u->playerproxy[i]);
+ }
+ else
+ {
+ sprintf(buf,"%-17s %-7s %-7s\n",
+ u->playername[i],u->playerteamtext[i],u->playerskin[i]);
+ }
+ q_print(u,buf);
+ }
+ }
+ else if(!memicmp(s,".fil",4))
+ { // message filter
+ uchar *p;
+ nextparam(&s);
+ if(*s)
+ {
+ int i,j=0;
+ for(i=0;i<15;i++)
+ {
+ p=nextparam(&s);
+ if(!*p) break;
+ if(*p!='#')
+ {
+ q_print(u,"group must start with '#'\n");
+ i=0;
+ break;
+ }
+ else
+ {
+ strncpy(u->filter[j],p,8);
+ u->filter[j][8]=0;
+ j++;
+ }
+ }
+ u->filternum=i;
+ }
+
+ q_print(u,"Your message filter groups:\n");
+ if(!u->filternum) q_print(u,"none\n");
+ else
+ {
+ int i;
+ for(i=0;i<u->filternum;i++)
+ {
+ q_printf(u,"%-10s",u->filter[i]);
+ if((i%3)==2) q_print(u,"\n");
+ }
+ if((i%3)!=0) q_print(u,"\n");
+ }
+ }
+#ifdef IDENT
+ else if(!memicmp(s,".ide",4))
+ {
+ Userinfo ui;
+ int num;
+
+ nextparam(&s);
+ if(*s=='0')
+ {
+ num=u->entity;
+ }
+ else if(*s=='f')
+ {
+ nextparam(&s);
+ strncpy(u->requestformat,s,63);
+ q_printf(u,"Ident format: %s\n",u->requestformat);
+ return;
+ }
+ else
+ {
+ num=atoi(findpoint(u,u->pos[0],u->pos[1],u->pos[2],u->ang[0],u->ang[1],u->ang[2],CAMP_PLAYER,POINT_TEAMMATENUMBER));
+ }
+
+// sprintf(tmpbuf,"num=%i act=%i team:%08X==%08X\n",
+// num,u->playeractive[num],u->playerteam[num],u->team);
+
+ if(num && u->playeractive[num] && u->playerteam[num]==u->team)
+ {
+ if(u->playerproxy[num]>=10000 || u->playerproxy[num]<211)
+ {
+ // user doesn't have a proxy with this feature
+ strcpy(u->msgline,u->playername[num]);
+ stror80(u->msgline);
+ u->time_nextcamp=u->time;
+ }
+ else
+ {
+ ui.type=USERINFO_REQUEST;
+ ui.requestfrom=num;
+ ui.requestlong=0;
+ u->time_request=u->time;
+ u->requestfrom=ui.requestfrom;
+
+ // create a setinfo command
+ sendinfomessage(u,encryptinfo(&ui));
+ }
+ }
+ }
+#endif
+ else
+ {
+ q_printf(u,"Unknown proxy command: %s\n",s);
+ }
+ }
+}
+
+
--- /dev/null
+// Cheapo - a QuakeWorld Proxy\r
+// Copyright (C) 1998 Sami Tammilehto\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r
+// See the GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+\r
+\r
+//----------------------------- compress.c ---------------------------\r
+\r
+#include "proxy.h"\r
+\r
+#ifdef COMPRESS\r
+\r
+static uchar *rw_bitp;\r
+static int rw_bit;\r
+\r
+void writebit(int bit)\r
+{ // bit must be 0 / 1\r
+ rw_bit|=bit;\r
+ if(rw_bit<0)\r
+ {\r
+ *rw_bitp++=rw_bit;\r
+ rw_bit=0x01000000;\r
+ }\r
+ else\r
+ {\r
+ rw_bit=rw_bit+rw_bit;\r
+ }\r
+}\r
+\r
+void writebits(int value,int bits)\r
+{\r
+ while(bits--)\r
+ {\r
+ writebit((value>>bits)&1);\r
+ }\r
+}\r
+\r
+void writebit_begin(uchar *d)\r
+{\r
+ rw_bitp=d;\r
+ rw_bit=0x01000000;\r
+}\r
+\r
+uchar *writebit_end(void)\r
+{ // returns first unused byte in stream\r
+ while(rw_bit!=0x01000000) writebit(0);\r
+ return(rw_bitp);\r
+}\r
+\r
+int readbit(void)\r
+{\r
+ if(rw_bit<0)\r
+ {\r
+ rw_bit=0x00800000|(*rw_bitp++);\r
+ }\r
+ rw_bit=rw_bit+rw_bit;\r
+ return( (rw_bit>>8) & 1);\r
+}\r
+\r
+int readbits(int bits)\r
+{\r
+ int value=0;\r
+ while(bits--)\r
+ {\r
+ value<<=1;\r
+ value|=readbit();\r
+ }\r
+ return(value);\r
+}\r
+\r
+void readbit_begin(uchar *d)\r
+{\r
+ rw_bitp=d;\r
+ rw_bit=-1;\r
+}\r
+\r
+uchar *readbit_pos(void)\r
+{ // returns first unused byte in stream\r
+ return(rw_bitp);\r
+}\r
+\r
+int compress_raw(uchar *d,uchar *s,int inbytes)\r
+{\r
+ memcpy(d,s,inbytes);\r
+ return(inbytes);\r
+}\r
+\r
+int uncompress_raw(uchar *d,uchar *s,int inbytes)\r
+{\r
+ memcpy(d,s,inbytes);\r
+ return(inbytes);\r
+}\r
+\r
+/**/\r
+\r
+int compress_header(User *u,uchar *d,uchar *s,int server)\r
+{\r
+ int seqthis,seqrecv,bitthis,bitrecv;\r
+\r
+ readheader(s,&seqthis,&seqrecv,&bitthis,&bitrecv);\r
+ *d++=(seqthis&0xff);\r
+ *d++=((seqthis>>8)&0x3f)|0x40|(bitthis?0x80:0);\r
+ *d++=(seqrecv&0xff);\r
+ *d++=((seqrecv>>8)&0x3f)|0x40|(bitrecv?0x80:0);\r
+\r
+ return(4);\r
+}\r
+\r
+int uncompress_header(User *u,uchar *d,uchar *s,int server)\r
+{\r
+ int seqthis,seqrecv,bitthis,bitrecv;\r
+ int flip=0;\r
+\r
+ seqthis = s[0];\r
+ seqthis|=(s[1]&0x3f)<<8;\r
+ bitthis = s[1]&0x80;\r
+\r
+ seqrecv = s[2];\r
+ seqrecv|=(s[3]&0x3f)<<8;\r
+ bitrecv = s[3]&0x80;\r
+\r
+ if(server)\r
+ {\r
+ seqthis|=u->server_lastthis&0xffffc000;\r
+ if((seqthis&0x3fff)<(u->server_lastthis&0x3fff))\r
+ {\r
+ flip=1;\r
+ seqthis+=0x4000;\r
+ }\r
+ seqrecv|=u->server_lastrecv&0xffffc000;\r
+ if((seqrecv&0x3fff)<(u->server_lastrecv&0x3fff))\r
+ {\r
+ flip=1;\r
+ seqrecv+=0x4000;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ seqthis|=u->client_lastthis&0xffffc000;\r
+ if((seqthis&0x3fff)<(u->client_lastthis&0x3fff))\r
+ {\r
+ flip=1;\r
+ seqthis+=0x4000;\r
+ }\r
+ seqrecv|=u->client_lastrecv&0xffffc000;\r
+ if((seqrecv&0x3fff)<(u->client_lastrecv&0x3fff))\r
+ {\r
+ flip=1;\r
+ seqrecv+=0x4000;\r
+ }\r
+ }\r
+\r
+ writeheader(d,&seqthis,&seqrecv,&bitthis,&bitrecv);\r
+\r
+ return(8);\r
+}\r
+\r
+int compress_client(User *u,uchar *d,uchar *s,int inbytes)\r
+{\r
+ inbytes=compress_raw(d,s+2,inbytes-2);\r
+ return(inbytes);\r
+}\r
+\r
+int uncompress_client(User *u,uchar *d,uchar *s,int inbytes)\r
+{\r
+ inbytes=uncompress_raw(d+2,s,inbytes);\r
+ d[0]=u->response_b&255;\r
+ d[1]=u->response_b>>8;\r
+ inbytes+=2;\r
+ return(inbytes);\r
+}\r
+\r
+int compress_server(User *u,uchar *d,uchar *s,int inbytes)\r
+{\r
+ compress_count(s,inbytes);\r
+ inbytes=compress_raw(d,s,inbytes);\r
+ return(inbytes);\r
+}\r
+\r
+int uncompress_server(User *u,uchar *d,uchar *s,int inbytes)\r
+{\r
+ inbytes=uncompress_raw(d,s,inbytes);\r
+ return(inbytes);\r
+}\r
+\r
+void uncompress(User *u,uchar *inbuf,int *inbytes,int server)\r
+{\r
+ int bytes=0,n;\r
+ int showcompressstatus=0;\r
+\r
+ if(intat(inbuf)==-1) return;\r
+\r
+ if(!(inbuf[3]&0x40))\r
+ {\r
+ if(!server && u->compress_server)\r
+ { // received an uncompressed client packet, stop packing\r
+ showcompressstatus=1;\r
+ u->compress_server=0;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // uncompress\r
+ if(server)\r
+ { // server packet\r
+ bytes+=uncompress_header(u,tmpbuf,inbuf,1);\r
+ n=uncompress_server(u,tmpbuf+bytes,inbuf+4,*inbytes-4);\r
+ if(n<0)\r
+ {\r
+ n=0;\r
+ u->compress_server=0;\r
+ u->compress_client=0;\r
+ q_print(u,"Server packet uncompress error. Compression disabled.\n");\r
+ }\r
+ bytes+=n;\r
+ }\r
+ else\r
+ { // client packet\r
+ // received a compressed client packet, start packing\r
+ if(!u->compress_server && !u->compress_test)\r
+ {\r
+ showcompressstatus=1;\r
+ u->compress_server=1;\r
+ }\r
+ bytes+=uncompress_header(u,tmpbuf,inbuf,0);\r
+ n=uncompress_client(u,tmpbuf+bytes,inbuf+4,*inbytes-4);\r
+ if(n<0)\r
+ {\r
+ n=0;\r
+ u->compress_server=0;\r
+ u->compress_client=0;\r
+ q_print(u,"Client packet uncompress error. Compression disabled.\n");\r
+ }\r
+ bytes+=n;\r
+ }\r
+\r
+ if(loglevel>=LOG_PACKETS)\r
+ {\r
+ if(server) logx(LOG_PACKETS,"uncompress-server");\r
+ else logx(LOG_PACKETS,"uncompress-client");\r
+ logx(LOG_PACKETS," %i -> %i bytes\n",\r
+ *inbytes,bytes);\r
+ }\r
+\r
+ memcpy(inbuf,tmpbuf,bytes);\r
+ *inbytes=bytes;\r
+ u->dumpsentpacket=1;\r
+ }\r
+\r
+ if(showcompressstatus)\r
+ {\r
+ if(u->compress_server) sprintf(tmpbuf,"Server compress: %s\n",u->proxyhostname);\r
+ else sprintf(tmpbuf,"Server compress: disabled\n",u->proxyhostname);\r
+ q_print(u,tmpbuf);\r
+ }\r
+}\r
+\r
+void compress(User *u,uchar *inbuf,int *inbytes,int server)\r
+{ // uses tmpbuf\r
+ int bytes=0;\r
+\r
+ if(intat(inbuf)==-1) return;\r
+\r
+ if(server)\r
+ { // server packet\r
+ if(!u->compress_server) return;\r
+ bytes+=compress_header(u,tmpbuf,inbuf,1);\r
+ bytes+=compress_server(u,tmpbuf+bytes,inbuf+8,*inbytes-8);\r
+ }\r
+ else\r
+ { // client packet\r
+ if(!u->compress_client) return;\r
+ bytes+=compress_header(u,tmpbuf,inbuf,0);\r
+ bytes+=compress_client(u,tmpbuf+bytes,inbuf+8,*inbytes-8);\r
+ }\r
+\r
+ if(loglevel>=LOG_PACKETS)\r
+ {\r
+ if(server) logx(LOG_PACKETS,"compress-server");\r
+ else logx(LOG_PACKETS,"compress-client");\r
+ logx(LOG_PACKETS," %i -> %i bytes\n",\r
+ *inbytes,bytes);\r
+ }\r
+\r
+ memcpy(inbuf,tmpbuf,bytes);\r
+ *inbytes=bytes;\r
+ u->dumpsentpacket=1;\r
+}\r
+\r
+#else\r
+\r
+void compress(User *u,uchar *inbuf,int *inbytes,int server)\r
+{\r
+}\r
+\r
+void uncompress(User *u,uchar *inbuf,int *inbytes,int server)\r
+{\r
+}\r
+\r
+#endif\r
+\r
--- /dev/null
+// Cheapo - a QuakeWorld Proxy
+// Copyright (C) 1998 Sami Tammilehto
+//
+// 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.
+
+#include "proxy.h"
+
+#define WS_VERSION_REQUIRED 0x0101
+#define MIN_SOCKETS_REQUIRED (MAXUSERS*2+4)
+
+int newsocketmode=0;
+uchar versionbuf[64];
+uchar versionnamebuf[64];
+uchar *version=versionbuf;
+uchar *versionname=versionnamebuf;
+
+SOCKADDR_IN proxy_serverip;
+SOCKADDR_IN proxy_clientip;
+
+#ifdef WIN32
+int useselect=0;
+#else
+int useselect=1;
+#endif
+
+int noautoexit=0;
+int litemode=0;
+int autotimeout=1;
+int loglevel=0;
+int lograwpackets=0;
+int originalloglevel=0;
+int lognpackets=0;
+int oldloglevel=0;
+int hpymode=0;
+
+int priority=0;
+int prioritytoggle=0;
+
+FILE *lograwc;
+FILE *lograws;
+
+int stopping=0;
+
+DWORD timeout=0x7fffffff;
+DWORD timeouttime=1000*60*240; // 240 minutes
+
+FILE *logfile;
+
+SOCKET consocket;
+
+int usernum=0;
+int maxusernum=MAXPLAYERS;
+User user[MAXUSERS];
+
+#ifdef EXPIRETIME
+uchar *versiontype="";
+char *betanotes=
+"\n"
+"release notes:\n"
+"\n";
+#else
+uchar *versiontype="";
+#endif
+
+void cleanup(int type);
+void setversions(void);
+
+#ifndef WIN32
+
+void Sleep(int ms)
+{
+ struct timeval delay;
+ delay.tv_sec=0;
+ delay.tv_usec=ms*1000;
+ select(0,NULL,NULL,NULL,&delay);
+}
+
+int memicmp(uchar *a,uchar *b,int len)
+{
+ while(len>0)
+ {
+ if(((*a)|0x20)!=((*b)|0x20)) return(1);
+ a++; b++; len--;
+ }
+ return(0);
+}
+
+int stricmp(uchar *a,uchar *b)
+{
+ int al,bl;
+ al=strlen(a);
+ bl=strlen(b);
+ if(al!=bl) return(1);
+ return(memicmp(a,b,al));
+}
+
+int strlwr(uchar *a)
+{
+ while(*a)
+ {
+ *a=tolower(*a);
+ a++;
+ }
+#if defined(__APPLE__) || defined(MACOSX)
+ return (0);
+#endif /* __APPLE__ || MACOSX */
+}
+
+#endif
+
+int timeus(void)
+{ // return microseconds (win95 only)
+#ifdef PERFTIME
+ LONGLONG t;
+ LARGE_INTEGER pc;
+ static int initdone;
+ static LARGE_INTEGER perf_freq,perf_counter;
+ if(!initdone)
+ {
+ if(!QueryPerformanceFrequency(&perf_freq))
+ {
+ printf("This computer does not support Pentium Performance Counter\n");
+ exit(3);
+ }
+ QueryPerformanceCounter(&perf_counter);
+ initdone=1;
+ }
+ QueryPerformanceCounter(&pc);
+ t=1000000*(pc.QuadPart-perf_counter.QuadPart)/perf_freq.QuadPart;
+ return((int)t);
+#else
+ return(0);
+#endif
+}
+
+int timems(void)
+{ // return milliseconds
+#ifdef GETTIMEOFDAY
+ struct timeval tv;
+ struct timezone tz;
+ static int initdone;
+ static int seconds0;
+ gettimeofday(&tv,&tz);
+ if(!initdone)
+ {
+ seconds0=tv.tv_sec;
+ initdone=1;
+ }
+ return((tv.tv_sec-seconds0)*1000+tv.tv_usec/1000);
+#endif
+#ifdef PERFTIME
+ LONGLONG t;
+ LARGE_INTEGER pc;
+ static int initdone;
+ static LARGE_INTEGER perf_freq,perf_counter;
+ if(!initdone)
+ {
+ if(!QueryPerformanceFrequency(&perf_freq))
+ {
+ printf("This computer does not support Pentium Performance Counter\n");
+ exit(3);
+ }
+ QueryPerformanceCounter(&perf_counter);
+ initdone=1;
+ }
+ QueryPerformanceCounter(&pc);
+ t=1000*(pc.QuadPart-perf_counter.QuadPart)/perf_freq.QuadPart;
+ return((int)t);
+#endif
+#ifdef HRTIME
+ hrtime_t tim;
+ static hrtime_t tim0;
+ static int initdone;
+ tim=gethrtime();
+ if(!initdone)
+ {
+ tim0=tim;
+ initdone=1;
+ }
+ tim-=tim0;
+ return((int)(tim/1000000));
+#endif
+#ifdef FTIME
+ struct timeb tim;
+ static int seconds0;
+ static int initdone;
+ int ms;
+ ftime(&tim);
+ if(!initdone)
+ {
+ seconds0=tim.time;
+ initdone=1;
+ }
+ ms=(tim.time-seconds0)*1000+tim.millitm;
+ return(ms);
+#endif
+#ifdef WINTIME
+ return(GetCurrentTime());
+#endif
+}
+
+#ifdef EXPIRETIME
+static int expires_time;
+#endif
+
+void expires_gettime(void)
+{
+#ifdef EXPIRETIME
+ time(&expires_time);
+#endif
+}
+
+void log_open(void)
+{
+ if(loglevel>0) logfile=fopen("proxy.log","wt");
+}
+
+void log_close(void)
+{
+ if(loglevel>0) fclose(logfile);
+}
+
+void log_flush(void)
+{
+ if(loglevel>0)
+ {
+ fflush(logfile);
+ }
+}
+
+static int dontflush;
+
+void logx(int level,uchar *txt,...)
+{
+ static uchar buf[BUFSIZE];
+ va_list argp;
+
+ if(loglevel<0) return;
+ if(level>LOG_CONSOLE && level>loglevel) return;
+ va_start(argp,txt);
+ vsprintf(buf,txt,argp);
+ if(level<=LOG_CONSOLE)
+ {
+ fputs(buf,stdout);
+ }
+ if(level<=loglevel && logfile)
+ {
+ fputs(buf,logfile);
+ if(level==LOG_CONSOLE) fflush(logfile);
+ }
+ if(!dontflush && loglevel==LOG_FLUSH) fflush(logfile);
+}
+
+void *mymalloc(int size)
+{
+ static void *safe;
+ void *p;
+
+ if(!size) size=1;
+
+ if(!safe)
+ { // allocate some safety (get core for us)
+ safe=malloc(256*1024); // 256K safety
+ if(!safe)
+ {
+ logx(LOG_CONSOLE,"Out of memory!\n");
+ printf("Out of memory!\n");
+ exit(3);
+ }
+ }
+
+ p=malloc(size);
+
+ if(!p)
+ {
+ logx(LOG_CONSOLE,"Running low on memory!\n");
+ // free safety and try to alloc again
+ if(safe)
+ {
+ free(safe);
+ }
+
+ p=malloc(size);
+ if(!p)
+ {
+ logx(LOG_CONSOLE,"Out of memory!\n");
+ printf("Out of memory!\n");
+ exit(3);
+ }
+ }
+
+ return(p);
+}
+
+void myfree(void *p)
+{
+ if(p) free(p);
+}
+
+void expires_check(void)
+{
+#ifdef EXPIRETIME
+ if(expires_time>EXPIRETIME+172800)
+ {
+ exit(3);
+ }
+#endif
+}
+
+void ws_init(void)
+{
+#ifdef WIN32
+ WSADATA wsaData;
+ int error;
+
+ error=WSAStartup(WS_VERSION_REQUIRED,&wsaData);
+ if (error !=0 ) {
+ logx(LOG_CONSOLE,"error: Windows Sockets not responding or compatible");
+ exit(3);
+ }
+
+ if (wsaData.wVersion < WS_VERSION_REQUIRED)
+ {
+ logx(LOG_CONSOLE,"error: Windows Sockets version %d.%d not supported",
+ LOBYTE (wsaData.wVersion), HIBYTE (wsaData.wVersion));
+ exit(3);
+ }
+
+ if (wsaData.iMaxSockets < MIN_SOCKETS_REQUIRED ) {
+ logx(LOG_CONSOLE,"error: Windows Sockets could not support %d sockets",
+ MIN_SOCKETS_REQUIRED);
+ exit(3);
+ }
+#endif
+}
+
+void ws_deinit(void)
+{
+#ifdef WIN32
+ int error;
+ error=WSACleanup();
+ if (error !=0 ) {
+ logx(LOG_CONSOLE,"error: Windows Sockets cleanup failed.");
+ exit(3);
+ }
+#endif
+}
+
+void ws_showerror(User *u,uchar *when)
+{
+#ifdef WIN32
+ int err=WSAGetLastError();
+ if(err==WSAENOTSOCK)
+ {
+ static int reportonce=0;
+ if(!reportonce)
+ {
+ logx(LOG_VERBOSE,"error: Windows Sockets error %d calling %s [once]\n",err,when);
+ reportonce=1;
+ }
+ return;
+ }
+ logx(LOG_VERBOSE,"error: Windows Sockets error %d calling %s\n",err,when);
+#endif
+ if(u) u->errors++;
+}
+
+int ws_openserver(int port)
+{
+// unsigned long flag;
+
+ consocket=socket(PF_INET,SOCK_DGRAM,0);
+ if(consocket==INVALID_SOCKET) ws_showerror(NULL,"socket");
+
+ if(bind(consocket,(LPSOCKADDR)&proxy_serverip,sizeof(proxy_serverip))==SOCKET_ERROR)
+ {
+ ws_showerror(NULL,"bind");
+ logx(LOG_CONSOLE,"Unable to bind to port %i\n",port);
+ return(1);
+ }
+
+// flag=1; // nonblocking
+// ioctlsocket(consocket,FIONBIO,&flag);
+ return(0);
+}
+
+void c_hexdump(uchar *msg,uchar *buffer0,int bytes,User *u)
+{
+ uchar *buffer=(uchar *)buffer0;
+ int i,j,n,s0,s1,a;
+
+ dontflush=1;
+ s0=intat(buffer+0);
+ s1=intat(buffer+4);
+ if(u)
+ {
+ logx(LOG_PACKETS,"\n%s [%s] (%i bytes): %i%c %i%c\n",
+ msg,u->name,bytes,
+ s0==0xffffffff?0:s0&0x7fffffff,s0&0x80000000?'x':'.',
+ s1==0xffffffff?0:s1&0x7fffffff,s1&0x80000000?'x':'.');
+ }
+ else
+ {
+ logx(LOG_PACKETS,"\n%s [] (%i bytes): %i%c %i%c\n",
+ msg,bytes,
+ s0==0xffffffff?0:s0&0x7fffffff,s0&0x80000000?'x':'.',
+ s1==0xffffffff?0:s1&0x7fffffff,s1&0x80000000?'x':'.');
+ }
+
+ n=(bytes+15)/16*16;
+ for(i=0;i<n;i+=16)
+ {
+ for(j=i;j<i+16;j++)
+ {
+ if(j<bytes) logx(LOG_PACKETS,"%02X ",buffer[j]);
+ else logx(LOG_PACKETS," ");
+ }
+ logx(LOG_PACKETS," ");
+ for(j=i;j<i+16;j++)
+ {
+ if(j<bytes)
+ {
+ a=buffer[j]&127;
+ if(a<32) logx(LOG_PACKETS,".");
+ else logx(LOG_PACKETS,"%c",a);
+ }
+ else logx(LOG_PACKETS," ");
+ }
+ logx(LOG_PACKETS,"\n");
+ }
+ if(lognpackets>0)
+ {
+ lognpackets--;
+ if(!lognpackets)
+ {
+ loglevel=oldloglevel;
+ }
+ }
+ dontflush=0;
+}
+
+User *c_finduser(SOCKADDR_IN *from)
+{
+ int i;
+
+ for(i=0;i<usernum;i++)
+ {
+ if(c_sameaddress(from,&user[i].toclientaddr)) return(user+i);
+ }
+ return(NULL);
+}
+
+void c_reopenserver(User *u)
+{
+ SOCKET news;
+ SOCKADDR_IN tmp;
+
+ if(newsocketmode) return;
+
+ news=socket(PF_INET,SOCK_DGRAM,0);
+ if(news==INVALID_SOCKET) ws_showerror(NULL,"socket2");
+ closesocket(u->toserver);
+ u->toserver=news;
+
+ memcpy(&tmp,&proxy_clientip,sizeof(SOCKADDR_IN));
+ tmp.sin_port=htons(0);
+ bind(u->toserver,(LPSOCKADDR)&tmp,sizeof(tmp));
+
+ {
+ char buf[64];
+ SOCKADDR_IN addr;
+ unsigned int addrlen=sizeof(SOCKADDR_IN);
+ memset(&addr,0,sizeof(addr));
+ getsockname(u->toserver,(void *)&addr,&addrlen);
+ c_textaddress(buf,&addr);
+ logx(LOG_PACKETS,"Toserver socket %s\n",buf);
+ }
+}
+
+User *c_newuser(SOCKADDR_IN *from)
+{
+ User *u;
+
+ if(usernum==MAXUSERS) return(NULL);
+
+ u=user+usernum;
+ memset(u,0,sizeof(User));
+
+ if(newsocketmode) u->toserver=consocket;
+ else
+ {
+ u->toserver=socket(PF_INET,SOCK_DGRAM,0);
+ if(u->toserver==INVALID_SOCKET) ws_showerror(NULL,"socket3");
+ }
+ u->toclientaddr=*from;
+
+ /*
+ {
+ SOCKADDR_IN tmp;
+ tmp.sin_family=AF_INET;
+ tmp.sin_addr.s_addr=htonl(INADDR_ANY);
+ tmp.sin_port=htons(0);
+ bind(u->toclient,(LPSOCKADDR)&tmp,sizeof(tmp));
+ }
+
+ {
+ char buf[64];
+ SOCKADDR_IN addr;
+ int addrlen=sizeof(SOCKADDR_IN);
+ memset(&addr,0,sizeof(addr));
+ getsockname(u->toclient,(void *)&addr,&addrlen);
+ c_textaddress(buf,&addr);
+ logx(LOG_PACKETS,"Toclient socket %s\n",buf);
+ }
+ */
+
+// flag=1; // nonblocking io
+// ioctlsocket(u->toserver,FIONBIO,&flag);
+// ioctlsocket(u->toclient,FIONBIO,&flag);
+
+ u->locations=0;
+ u->locationsmax=256;
+ u->loc=mymalloc(sizeof(Location)*u->locationsmax);
+ memset(u->loc,0,sizeof(Location)*u->locationsmax);
+
+ usernum++;
+ return(u);
+}
+
+void c_killuser(User *u)
+{
+ int i,uid;
+
+ uid=u-user;
+ if(uid<0 || uid>=MAXUSERS) return;
+
+ if(!newsocketmode)
+ {
+ closesocket(u->toserver);
+ //closesocket(u->toclient);
+ }
+
+ for(i=0;i<MAXMODELS;i++)
+ {
+ if(u->model[i]) free(u->model[i]);
+ }
+ for(i=0;i<MAXSOUNDS;i++)
+ {
+ if(u->sound[i]) free(u->sound[i]);
+ }
+ for(i=0;i<u->locationsmax;i++)
+ {
+ if(u->loc[i].name) free(u->loc[i].name);
+ }
+ free(u->loc);
+ u->loc=NULL;
+
+ memcpy(user+uid,user+usernum-1,sizeof(User));
+ usernum--;
+}
+
+void c_killserver(User *u)
+{
+ SOCKET news;
+// int flag;
+
+ if(!newsocketmode)
+ {
+ news=socket(PF_INET,SOCK_DGRAM,0);
+
+ closesocket(u->toserver);
+ u->toserver=news;
+ }
+
+ u->time_lastserver=0;
+
+ u->servermainaddr.sin_port=0;
+
+// flag=1; // nonblocking io
+// ioctlsocket(u->toserver,FIONBIO,&flag);
+}
+
+void c_textaddress(uchar *text,SOCKADDR_IN *a)
+{
+ if(a->sin_port)
+ {
+ strcpy(text,inet_ntoa(a->sin_addr));
+ sprintf(text+strlen(text),":%i",ntohs(a->sin_port));
+ }
+ else strcpy(text,"-");
+}
+
+int c_sameaddress(SOCKADDR_IN *a,SOCKADDR_IN *b)
+{
+ if(a->sin_addr.s_addr!=b->sin_addr.s_addr) return(0);
+ if(a->sin_port!=b->sin_port) return(0);
+ return(1);
+}
+
+int c_islocalconnect(SOCKADDR_IN *a)
+{
+ unsigned int lh,lhm;
+ if(a->sin_port!=htons(27001)) return(0);
+ lh =htonl(0x7f000000);
+ lhm=htonl(0xff000000);
+ if((a->sin_addr.s_addr&lhm)==lh) return(1);
+ return(0);
+}
+
+void c_setaddress(SOCKADDR_IN *a,uchar *text)
+{
+ uchar addr[256];
+ uchar port[8];
+ uchar *p;
+ struct hostent *he;
+
+ logx(LOG_DEBUG,"Namelookup '%s'\n",text);
+
+ p=addr;
+ while(*text && *text>32 && *text!=':' && p<addr+255)
+ {
+ *p++=*text++;
+ }
+ if(*text==':') text++;
+ *p=0;
+
+ p=port;
+ while(*text && p<port+7)
+ {
+ *p++=*text++;
+ }
+ *p=0;
+
+ a->sin_family=AF_INET;
+ a->sin_port=htons((unsigned short)atoi(port));
+ if(!a->sin_port) a->sin_port=htons(27500);
+
+ a->sin_addr.s_addr=inet_addr(addr);
+ if(a->sin_addr.s_addr==-1)
+ { // not xxx.xxx.xxx.xxx, do lookup
+ he=gethostbyname(addr);
+ if(!he)
+ { // dns failed
+ a->sin_addr.s_addr=0;
+ }
+ else
+ {
+ memcpy(&a->sin_addr,he->h_addr_list[0],sizeof(struct in_addr));
+ }
+ }
+
+ if(a->sin_addr.s_addr==0xffffffff || a->sin_addr.s_addr==0x0)
+ {
+ a->sin_addr.s_addr=0;
+ a->sin_port=0;
+ }
+
+ logx(LOG_DEBUG,"Namelookup result %08X:%i\n",a->sin_addr.s_addr,atoi(port));
+}
+
+void c_sendservermain(User *u,uchar *buffer,int bytes)
+{
+ int error;
+ if(loglevel>=LOG_PACKETS) c_hexdump("Proxy->ServerConnect",buffer,bytes,u);
+ error=sendto(u->toserver,buffer,bytes,0,(LPSOCKADDR)&u->servermainaddr,sizeof(SOCKADDR_IN));
+ if(error==SOCKET_ERROR)
+ {
+ uchar tmp[64];
+ c_textaddress(tmp,&u->servermainaddr);
+ ws_showerror(u,"sendservermain");
+ logx(LOG_CONSOLE,"servermainaddr: %s",tmp);
+ }
+}
+
+void c_sendserver(User *u,uchar *buffer,int bytes)
+{
+ int error;
+ if(lograwpackets && u->state==STATE_CONNECTED)
+ {
+ fwrite(&bytes,1,4,lograwc);
+ fwrite(buffer,1,bytes,lograwc);
+ }
+ u->bytestoserversec1+=bytes;
+ u->bytestoserver+=bytes;
+ if(u->dumpsentpacket) if(loglevel>=LOG_PACKETS) c_hexdump("Proxy->Server",buffer,bytes,u);
+ u->dumpsentpacket=0;
+ error=sendto(u->toserver,buffer,bytes,0,(LPSOCKADDR)&u->toserveraddr,sizeof(SOCKADDR_IN));
+ if(error==SOCKET_ERROR) ws_showerror(u,"sendserver");
+}
+
+void c_sendclient(User *u,uchar *buffer,int bytes)
+{
+ int error;
+ if(lograwpackets && u->state==STATE_CONNECTED)
+ {
+ fwrite(&bytes,1,4,lograws);
+ fwrite(buffer,1,bytes,lograws);
+ }
+ u->bytestoclientsec1+=bytes;
+ u->bytestoclient+=bytes;
+ if(u->dumpsentpacket) if(loglevel>=LOG_PACKETS) c_hexdump("Proxy->Client",buffer,bytes,u);
+ u->dumpsentpacket=0;
+ error=sendto(consocket,buffer,bytes,0,(LPSOCKADDR)&u->toclientaddr,sizeof(SOCKADDR_IN));
+ if(error==SOCKET_ERROR) ws_showerror(u,"sendclient");
+}
+
+void c_sendto(SOCKADDR_IN *to,uchar *buffer,int bytes)
+{
+ int error;
+ error=sendto(consocket,buffer,bytes,0,(LPSOCKADDR)to,sizeof(SOCKADDR_IN));
+ if(error==SOCKET_ERROR) ws_showerror(NULL,"sendto");
+}
+
+int startqwcl_nochecks(char *cmd, char *cmdline)
+{
+#ifdef WIN32
+ char execmdline[2048];
+ char exename[1024];
+ char exepath[1024];
+ char exedir[1024];
+ char *exepathname;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ if(strlen(cmd)>256) return(5);
+ if(strlen(cmdline)>1024) return(5);
+
+ strcpy(exename,cmd);
+ if(strlen(exename)<4 || stricmp(exename+strlen(exename)-4,".exe") )
+ {
+ strcat(exename,".EXE");
+ }
+ if(!GetFullPathName(exename, 1024, exepath, &exepathname))
+ {
+ return(1);
+ }
+ strcpy(exedir,exepath);
+ exedir[exepathname-exepath]=0;
+
+ strcpy(execmdline,exepath);
+ strcat(execmdline," ");
+ strcat(execmdline,cmdline);
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+
+ if(!CreateProcess(exepath,execmdline,0,0,TRUE,0,0,exedir,&si,&pi))
+ {
+ return 4;
+ }
+
+ WaitForSingleObject(pi.hProcess,INFINITE);
+#endif
+ return 0;
+}
+
+int mainloop_startqw(int doonce)
+{ // low priority
+#ifdef WIN32
+ int ret;
+#endif /* WIN32 */
+
+ logx(LOG_DEBUG,"mainloop_startqw started\n");
+#ifdef WIN32
+ if(!*quakepath) return(0);
+
+ sprintf(quakeoptions+strlen(quakeoptions)," +connect 127.1:%i",proxy_port);
+
+ if(gamespy)
+ {
+ //strcat(quakeoptions," +exec GameSpyC.cfg");
+ printf("Cheapo started from GameSpy.\n");
+ }
+
+ printf("Starting QWCL (%s)\n",quakeoptions);
+#ifdef PROTECT
+ ret=startqwcl(quakepath,quakeoptions);
+#else
+ ret=startqwcl_nochecks(quakepath,quakeoptions);
+#endif
+ if(ret)
+ {
+ if(ret==1) printf("error: Could not find executable.\n");
+ else if(ret==2) printf("error: The executable is modified.\n");
+ else printf("error: Could not start executable\n",ret);
+ cleanup(0);
+ return(0);
+ }
+#ifdef PROTECT
+ if(quakemodified)
+ {
+ printf("warning: Executable is modified (f_modified will report this).\n");
+ }
+#endif
+
+ if(!noautoexit)
+ {
+ printf("QWCL has been closed: ");
+ cleanup(0);
+ }
+#endif
+ return(0);
+}
+
+void setpriorityx(int fast)
+{
+#ifdef WIN32
+ HANDLE thread; // mainloop thread
+ HANDLE process; // mainloop thread
+ process=GetCurrentProcess();
+ thread=GetCurrentThread();
+
+ if(fast)
+ {
+ SetPriorityClass(process,HIGH_PRIORITY_CLASS);
+ if(!priority)
+ {
+ SetThreadPriority(thread,THREAD_PRIORITY_HIGHEST);
+ }
+ else
+ {
+ SetThreadPriority(thread,priority-13);
+ }
+ }
+ else
+ {
+ SetPriorityClass(process,NORMAL_PRIORITY_CLASS);
+ SetThreadPriority(thread,THREAD_PRIORITY_BELOW_NORMAL);
+ }
+#endif
+}
+
+int mainloop_low(int doonce)
+{ // low priority
+ int i;
+ DWORD time;
+ User *u;
+
+ logx(LOG_DEBUG,"mainloop_low started\n");
+
+#ifdef WIN32
+ SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_LOWEST);
+#endif
+
+ while(!stopping)
+ {
+ // handle autotimeout
+ time=timems();
+ if(autotimeout && time>timeout) stopping=1;
+
+ for(i=0;i<usernum;i++)
+ {
+ u=user+i;
+ if(u->state==STATE_NAMELOOKUP)
+ {
+ logx(LOG_DEBUG,"main lookup\n");
+ c_setaddress(&u->servermainaddr,u->connecthere);
+ u->state=STATE_TRYING;
+ }
+ }
+ if(doonce) return(0);
+
+ Sleep(250); // 0.25 sec
+ }
+ return(0);
+}
+
+int mainloop_high(int value)
+{ // high priority
+#if defined(__APPLE__) || defined(MACOSX)
+ User *u = NULL;
+#else
+ User *u;
+#endif /* __APPLE__ || MACOSX */
+ int bytes;
+ SOCKADDR_IN from;
+ unsigned int fromsize=sizeof(from);
+ int error,i;
+#if !defined(__APPLE__) && !defined(MACOSX)
+ int cliopen=0;
+#endif /* !__APPLE__ && !MACOSX */
+ fd_set readset;
+ struct timeval delay;
+ DWORD time;
+ static uchar buffer[BUFSIZE];
+ int hh;
+ // stats
+#ifdef MAINPROFILE
+ DWORD time1,time2;
+ DWORD nextprinttime=0;
+ DWORD timesum=0,timecnt=0;
+ DWORD timemax=0;
+#endif
+
+ logx(LOG_DEBUG,"mainloop_high started\n");
+
+ setpriorityx(2);
+
+ timeout=timems()+timeouttime;
+
+ while(!stopping)
+ {
+ hh=0;
+ FD_ZERO(&readset);
+ FD_SET(consocket,&readset);
+#ifndef WIN32
+ hh=consocket+1;
+#endif
+ for(i=0;i<usernum;i++)
+ {
+ if(user[i].state!=STATE_HERE)
+ {
+ FD_SET(user[i].toserver,&readset);
+#ifndef WIN32
+ if(user[i].toserver>=hh) hh=user[i].toserver+1;
+#endif
+ }
+ }
+
+ delay.tv_sec=1;
+ delay.tv_usec=0;
+
+ if(prioritytoggle) setpriorityx(0);
+ error=select(hh,&readset,NULL,NULL,&delay);
+ if(prioritytoggle) setpriorityx(1);
+// if(loglevel>=LOG_PACKETS) logx(LOG_PACKETS,"\n\n\nSelect returns with %i\n",error);
+ if(error==SOCKET_ERROR)
+ {
+ ws_showerror(u,"select");
+ }
+
+ time=timems(); // milliseconds
+#ifdef MAINPROFILE
+ time1=timeus();
+#endif
+ if(FD_ISSET(consocket,&readset))
+ {
+ int i;
+ timeout=time+timeouttime;
+ // contact to server port
+ bytes=recvfrom(consocket,buffer,sizeof(buffer)-1,0,
+ (LPSOCKADDR)&from,&fromsize);
+ buffer[bytes]=0;
+
+ if(bytes==SOCKET_ERROR)
+ {
+ ws_showerror(u,"connect");
+ }
+ else
+ {
+ // check if it is from a connected client
+ for(i=0;i<usernum;i++)
+ {
+ globalu=u=user+i;
+ u->time=time;
+
+ if(c_sameaddress(&u->toclientaddr,&from))
+ {
+ if(u->time>u->time_bytestosec)
+ {
+ u->bytestoserversec=u->bytestoserversec1;
+ u->bytestoclientsec=u->bytestoclientsec1;
+ u->bytestoserversec1=0;
+ u->bytestoclientsec1=0;
+ if(u->bytestoserversec>u->bytestoserversecmax) u->bytestoserversecmax=u->bytestoserversec;
+ if(u->bytestoclientsec>u->bytestoclientsecmax) u->bytestoclientsecmax=u->bytestoclientsec;
+ u->time_bytestosec=u->time+1000;
+ }
+
+ u->time_lastclient=time;
+
+ if(u->quiet>0) u->quiet--;
+ else u->quiet=0;
+
+ if(!memcmp(buffer,"\xff\xff\xff\xffgetcha",10))
+ {
+ u->state=STATE_DROPPING;
+ break;
+ }
+
+ if(u->state==STATE_DISCONNECTING)
+ {
+ q_command(u,"!sdrop");
+ }
+ else if(u->state==STATE_TRYING)
+ {
+ if(u->time>u->time_nexttry) q_command(u,"!try");
+ }
+ else if(u->state==STATE_INIT)
+ {
+ q_command(u,"!init");
+ }
+ else if(u->state==STATE_HERE)
+ {
+ if(u->doconnect)
+ {
+ if(u->doconnect>1) u->doconnect--;
+ else
+ {
+ q_command(u,"!connect");
+ u->doconnect=0;
+ }
+ }
+ }
+ else
+ {
+ if(u->donew)
+ {
+ if(u->donew>1) u->donew--;
+ else
+ {
+ q_command(u,"!sendnew");
+ //u->donew=25; // send again in 25 cycles if not cancelled by fullserverinfo
+ u->donew=0;
+ }
+ }
+ }
+
+ if(loglevel>=LOG_PACKETS) c_hexdump("\n\nClient->Proxy",buffer,bytes,u);
+ if(u->state>=STATE_CONNECTED)
+ {
+ q_client(u,buffer,bytes);
+ }
+ else
+ {
+ q_proxy(u,buffer,bytes);
+ }
+
+ break;
+ }
+ }
+
+ if(i==usernum)
+ { // user not found
+ if(buffer[0]==0xff && buffer[1]==0xff && buffer[2]==0xff && buffer[3]==0xff)
+ {
+ if(loglevel>=LOG_PACKETS) c_hexdump("Someone->ProxyConnect",buffer,bytes,NULL);
+ q_connect(buffer,bytes,&from);
+ }
+ else
+ { // unrecognized packet from someone
+ if(hpymode)
+ {
+ int besti,bestv,value;
+ bestv=0x10000000;
+ besti=-1;
+ // find best match
+ for(i=0;i<usernum;i++)
+ {
+ value=packetmatch(user+i,buffer);
+ if(value<bestv)
+ {
+ bestv=value;
+ besti=i;
+ }
+ }
+ if(besti>=0)
+ {
+ u=user+besti;
+
+ memcpy(&u->toclientaddr,&from,sizeof(SOCKADDR_IN));
+ //q_print(u,"proxy: dynamic ip change detected\n");
+ q_printf(u,"proxy: ip change for %s\n",u->name);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for(i=0;i<usernum;i++)
+ {
+ globalu=u=user+i;
+
+ u->time=time;
+
+ if(FD_ISSET(u->toserver,&readset))
+ {
+ timeout=time+timeouttime;
+ bytes=recvfrom(u->toserver,buffer,sizeof(buffer)-1,0,(LPSOCKADDR)&from,&fromsize);
+ u->time_lastserver=time;
+ buffer[bytes]=0;
+ if(bytes==SOCKET_ERROR)
+ {
+ ws_showerror(u,"receiveserver");
+ }
+ else
+ {
+ if(loglevel>=LOG_PACKETS) c_hexdump("\n\nServer->Proxy",buffer,bytes,u);
+ u->toserveraddr=from;
+
+ q_server(u,buffer,bytes);
+ }
+ }
+ }
+
+ // process dead users (also timeouts)
+ // process dropping
+ for(i=0;i<usernum;i++)
+ {
+#if !defined(__APPLE__) && !defined(MACOSX)
+ static DWORD lasttime;
+#endif /* !__APPLE__ && !MACOSX */
+ globalu=u=user+i;
+
+ if(u->errors>MAXERRORS)
+ { // too many errors (handle as timeout)
+ q_command(u,"!errors");
+ i=0; // user order has changed
+ }
+ if(u->time_lastclient>0 && u->time>u->time_lastclient+TIMEOUT)
+ { // client timeout
+ q_command(u,"!timeout");
+ i=0; // user order has changed
+ }
+ if(u->state==STATE_DROPPING)
+ {
+ q_command(u,"!kill");
+ i=0; // user order has changed
+ }
+ }
+
+#ifdef MAINPROFILE
+ time2=timeus();
+ time2-=time1;
+ if(time2>timemax) timemax=time2;
+ timesum+=time2;
+ timecnt++;
+
+ if(time>nextprinttime)
+ {
+ nextprinttime=time+1000;
+
+ if(timecnt>0) timesum/=timecnt;
+ printf("Process time: avg %2ius max %2ius\n",timesum,timemax);
+
+ timemax=0;
+ timecnt=0;
+ timesum=0;
+ }
+#endif
+
+ if(autotimeout && time>timeout) stopping=1;
+ }
+ return(0);
+}
+
+int startup(int port)
+{
+ log_open();
+ logx(LOG_DEBUG,"startup %i\n",port);
+ ws_init();
+ if(ws_openserver(port)) return(1);
+ return(0);
+}
+
+void cleanup(int type)
+{
+ static int cleanupdone=0;
+ if(cleanupdone) return;
+ cleanupdone=1;
+ stopping=1;
+ log_flush();
+ printf("Terminating...\n");
+ Sleep(500);
+ ws_deinit();
+ log_close();
+ if(lograwpackets)
+ {
+ fclose(lograwc);
+ fclose(lograws);
+ }
+#ifdef WIN32
+ ExitProcess(0);
+#endif
+}
+
+void expires_startup(void)
+{
+#ifdef EXPIRETIME
+ int tim;
+
+// printf("\nTHIS IS A SPECIAL NONPUBLIC TEST VERSION (Sir-BETA).\n");
+ printf("\nTHIS IS A SPECIAL NONPUBLIC TEST VERSION, DO NOT DISTRIBUTE.\n");
+
+ time(&tim);
+ if(tim>EXPIRETIME+0)
+ {
+ printf("This version has expired. A newer version should be available.\n");
+ exit(3);
+ }
+
+#endif
+}
+
+void setversions(void)
+{
+ int versionnamesrc[16];
+ int versionsrc[16];
+ int i,j;
+ i=0;
+ j=120+0x40800000;
+ versionnamesrc[i++]=(j++)+'C';
+ versionnamesrc[i++]=(j++)+'h';
+ versionnamesrc[i++]=(j++)+'e';
+ versionnamesrc[i++]=(j++)+'a';
+ versionnamesrc[i++]=(j++)+'p';
+ versionnamesrc[i++]=(j++)+'o';
+ versionnamesrc[i++]=0;
+ i=0;
+ j=120+0x40800000;
+ versionsrc[i++]=(j++)+'2';
+ versionsrc[i++]=(j++)+'.';
+ versionsrc[i++]=(j++)+'6';
+ versionsrc[i++]=(j++)+'1';
+ versionsrc[i++]=0;
+ {
+ int i;
+ char *d;
+
+ d=version;
+ for(i=0;i<16;i++)
+ {
+ if(!versionsrc[i]) break;
+ *d++=(versionsrc[i]&255)-(120+i);
+ }
+ *d=0;
+
+ d=versionname;
+ for(i=0;i<16;i++)
+ {
+ if(!versionnamesrc[i]) break;
+ *d++=(versionnamesrc[i]&255)-(120+i);
+ }
+ *d=0;
+ }
+}
+
+void convertspyconfig(char *fin,char *fout)
+{
+ FILE *f1,*f2;
+ char buf[256];
+
+ f1=fopen(fin,"rb");
+ if(!f1) return;
+ f2=fopen(fout,"wb");
+ if(!f2)
+ {
+ fclose(f1);
+ return;
+ }
+
+ fprintf(f2,"wait\n");
+ while(!feof(f1))
+ {
+ *buf=0;
+ fgets(buf,255,f1);
+ if(!memcmp(buf,"connect ",8))
+ {
+ putc('s',f2);
+ memcpy(buf,"ay .con ",8);
+ }
+ fputs(buf,f2);
+ }
+
+ fclose(f2);
+ fclose(f1);
+}
+
+int main2(int argc,uchar *argv[])
+{
+ int port;
+ int error=0,a,showinternal=0;
+ int mset=0,aset=0,csset=0,nset=0,pwset=0;
+ uchar *p;
+
+ proxy_serverip.sin_family=AF_INET;
+ proxy_serverip.sin_addr.s_addr=INADDR_ANY;
+ proxy_serverip.sin_port=27500;
+
+ proxy_clientip.sin_family=AF_INET;
+ proxy_clientip.sin_addr.s_addr=INADDR_ANY;
+ proxy_clientip.sin_port=0;
+
+ port=27500;
+ loglevel=-1;
+
+ setversions();
+ for(a=1;a<argc;a++)
+ {
+ if(*(argv[a])=='/' || *(argv[a])=='-') switch(*(argv[a]+1))
+ {
+ case 'h' :
+ case '?' : if(argv[a][2]=='p') error=2;
+ else error=1;
+ break;
+
+ case '!' : showinternal=1; break;
+
+ case 'p' : p=argv[++a];
+ if(!p) break;
+ port=atoi(p);
+ break;
+
+ case 'm' : p=argv[++a];
+ if(!p) break;
+ maxusernum=atoi(p);
+ if(maxusernum>MAXPLAYERS) maxusernum=MAXPLAYERS;
+ if(maxusernum<1) maxusernum=1;
+ mset=1;
+ break;
+
+ case 'a' : p=argv[++a];
+ if(!p || !*p) break;
+ strncpy(proxy_allowip,p,MAXSTR-1);
+ aset=1;
+ break;
+
+ case 'd' : p=argv[++a];
+ if(!p || !*p) break;
+ if(proxy_denyipnum<16)
+ {
+ strncpy(proxy_denyip[proxy_denyipnum],p,MAXSTR-1);
+ proxy_denyipnum++;
+ }
+ aset=1;
+ break;
+
+ case 'c' : p=argv[++a];
+ if(!p || !*p) break;
+ c_setaddress(&proxy_clientip,p);
+ csset=1;
+ break;
+
+ case 'o' : p=argv[++a];
+ if(!p || !*p) break;
+ strcpy(proxy_name,p);
+ nset=1;
+ break;
+
+ case 'w' : p=argv[++a];
+ if(!p || !*p) break;
+ strcpy(proxy_password,p);
+ pwset=1;
+ break;
+
+ case 's' : p=argv[++a];
+ if(!p || !*p) break;
+ c_setaddress(&proxy_serverip,p);
+ csset=1;
+ break;
+
+ case 'q' : loglevel=-1;
+ break;
+
+ case 'x' : noautoexit=1;
+ break;
+
+ case 'n': autotimeout=0;
+ break;
+
+ // case 'z': newsocketmode=0;
+ // break;
+
+ case 'y': hpymode=1;
+ break;
+
+ case 'j': prioritytoggle=1;
+ priority=13;
+ case 'i' : p=argv[++a];
+ if(!p) break;
+ priority=atoi(p);
+ if(priority<11 || priority>15)
+ {
+ printf("WARNING: -i parameter should be in range 11..15\n");
+ }
+ break;
+
+ // case 'i': litemode=FPD_TIMERS|FPD_PERCENTE|FPD_SKIN;
+ // break;
+
+ case 'l' : p=argv[++a];
+ if(!p) break;
+ loglevel=atoi(p);
+ #ifdef PROMODE
+ if(loglevel==-1)
+ {
+ loglevel=2;
+ lograwpackets=1;
+ lograwc=fopen("client.raw","wb");
+ lograws=fopen("server.raw","wb");
+ printf("Logging raw packets to CLIENT.RAW, SERVER.RAW\n");
+ if(!lograwc) exit(1);
+ if(!lograws) exit(1);
+ }
+ #endif
+ break;
+ default :
+ error=1;
+ break;
+ }
+ else
+ {
+#ifdef WIN32
+ char *p;
+ strcpy(quakepath,argv[a]);
+ strcpy(quakeoptions,strstr(GetCommandLine(), argv[a]));
+ if(p=strstr(quakeoptions,"+exec GameSpy.cfg"))
+ {
+ *p=0;
+ gamespy=1;
+ convertspyconfig("id1\\GameSpy.cfg","id1\\GameSpyC.cfg");
+ }
+#else
+ error=1;
+#endif
+ break;
+ }
+ }
+
+ if(litemode)
+ {
+ strcat(version," lite");
+ }
+
+ proxy_serverip.sin_port=htons((unsigned short)port);
+
+#ifdef PROMODE
+ versiontype=" open-pro";
+#else
+ versiontype=" open";
+#endif
+
+ printf("\n"
+ "%s V%s%s - Freeware QuakeWorld Proxy - (C) 1998-1999 Sami Tammilehto.\n"
+ "Unsupported software. Absolutely no warranty. Use at your own risk.\n"
+ ,versionname,version,versiontype);
+
+ expires_startup();
+ if(error)
+ {
+ printf("\n"
+#ifdef WIN32
+ "usage: cheapo [options...] [qwcl.exe path] [qwcl options]\n"
+#else
+ "usage: cheapo [options...]\n"
+#endif
+ "options:\n"
+ "-p <port> port to listen to (default 27500)\n"
+ "-m <num> set maxclients (1-16)\n"
+ "-o <name> server host name\n"
+ "-w <pass> password required to connect\n"
+ "-s <ip> server interface (which ip proxy listens)\n"
+ "-c <ip> client interface (which ip proxy uses to connect)\n"
+ "-a <ip> only accept connections with ip addresses starting like this\n"
+ " (so -a 192.168 would accept connections only from 192.168.x.x)\n"
+ "-d <ip> deny connections from ip addresses (like -a). Multiple allowed.\n"
+#ifdef WIN32
+ "-i <pri> set windows priority (11-15)\n"
+ "-j drop priority for select\n"
+#endif
+ "-l <level> log to file proxy.log, level is 0..2\n"
+ "-q quiet; no screen output after startup\n"
+ " (now default, use '-l 0' to enable connect/disconnect reports)\n"
+ "-n no timeout (otherwise timeouts after 4 hours of inactivity)\n"
+ /*
+ "-z use older TCP/IP routines (like in V2.4) instead of the new ones\n"
+ " (if you get problems or more lag with this new version)\n"
+ */
+ "-x no proxy exit when QW exits (and has been started by the Proxy)\n"
+ "-y allow client IP changes (so that a reconnect using dynamic\n"
+ " IP numbers doesn't disconnect you from the proxy)\n"
+ "\n"
+ "You can automatically start QuakeWorld by specifying the QWCL path and options\n"
+ "like 'cheapo c:\\quake\\qwcl.exe'. This is required for f_modified to work.\n"
+ "When you quit QWCL in this mode, the proxy also exits (unless -x is used).\n"
+ "\n");
+
+ return(0);
+ }
+
+ printf("\n");
+
+ if(csset)
+ {
+ c_textaddress(tmpbuf,&proxy_serverip);
+ if(*tmpbuf!='-') printf("Server Interface %s. ",tmpbuf);
+ c_textaddress(tmpbuf,&proxy_clientip);
+ if(*tmpbuf!='-') printf("Client Interface %s. ",tmpbuf);
+ printf("\n");
+ }
+ if(nset) printf("Proxy hostname '%s'.\n",proxy_name);
+ if(hpymode)
+ {
+ if(maxusernum>1) printf("Dynamic client IP changes allowed.\n");
+ else printf("Dynamic client IP changes allowed.\n");
+ }
+ if(pwset) printf("Proxy password set.\n");
+ if(mset) printf("Max %i users allowed to connect.\n",maxusernum);
+ if(aset) printf("IP checking (-a/-d) enabled.\n");
+ if(priority) printf("Using adjusted windows priority.\n");
+ if(autotimeout) printf("Proxy exits after 4 hours of inactivity (-n disables autoexit)\n");
+#ifndef PACKETLOG
+ if(loglevel>2)
+ {
+ loglevel=2;
+ printf("NOTE: maximum loglevel is 2\n");
+ }
+#endif
+ if(loglevel>0) printf("Logging to \'proxy.log\' (loglevel %i).\n",loglevel);
+
+#ifdef PROMODE
+ printf("compiled with: PROMODE\n");
+#endif
+#ifdef COMPRESSION
+ printf("compiled with: COMPRESSION\n");
+#endif
+#ifdef PACKETLOG
+ printf("compiled with: PACKETLOG\n");
+#endif
+#ifdef IDENT
+ printf("compiled with: IDENT\n");
+#endif
+#ifdef SPEED
+ printf("compiled with: .SHOW\n");
+#endif
+
+#ifdef EXPIRETIME
+ printf(betanotes);
+#endif
+
+ if(startup(port))
+ {
+ printf("Could not bind to port %i.\n\n",port);
+ return(1);
+ }
+
+ printf("Listening to port %i (press CTRL-C to stop).\n",port);
+ proxy_port=port;
+ fflush(stdout);
+
+ originalloglevel=loglevel;
+
+ timeout=timems()+timeouttime;
+
+#ifdef WIN32
+ {
+ HANDLE thread; // mainloop thread
+ DWORD threadid;
+
+ SetConsoleCtrlHandler((PHANDLER_ROUTINE)cleanup,TRUE);
+
+ if(newsocketmode)
+ {
+/*
+ thread=CreateThread(NULL, // security settings
+ 0, // stack size
+ (LPTHREAD_START_ROUTINE)mainloop_high_newbug, // routine
+ NULL, // parameter (not used)
+ 0, // creationflags
+ &threadid); // thread id
+*/
+ }
+ else
+ {
+ thread=CreateThread(NULL, // security settings
+ 0, // stack size
+ (LPTHREAD_START_ROUTINE)mainloop_high, // routine
+ NULL, // parameter (not used)
+ 0, // creationflags
+ &threadid); // thread id
+ }
+// SetThreadPriority(thread,THREAD_PRIORITY_HIGHEST);
+
+ if(*quakepath)
+ {
+ thread=CreateThread(NULL, // security settings
+ 0, // stack size
+ (LPTHREAD_START_ROUTINE)mainloop_startqw, // routine
+ NULL, // parameter (not used)
+ 0, // creationflags
+ &threadid); // thread id
+ }
+
+ mainloop_low(0);
+ }
+#else
+ mainloop_high(0);
+#endif
+
+ Sleep(1000);
+ cleanup(0);
+
+ return(0);
+}
+
+/******************************************/
+
+void statecrc(User *u,char *result)
+{
+#ifdef PROTECT
+ // REMOVED: 3 letter version/connection checksum
+#else
+ strcpy(result,"<open>");
+#endif
+}
+
+void codecrc(char *salt,char *result)
+{
+#ifdef PROTECT
+ // REMOVED: code/fmodified checksum
+#else
+ strcpy(result,"<open>");
+#endif
+}
+
+#if defined(__APPLE__) || defined(MACOSX)
+
+int main(int argc,char **argv)
+
+#else
+
+int main(int argc,uchar *argv[])
+
+#endif /* __APPLE__ || MACOSX */
+
+{
+#ifdef PROTECT
+ // REMOVED: exe integrity check
+#endif
+
+#if defined(__APPLE__) || defined(MACOSX)
+
+ return(main2(argc,(uchar **) argv));
+
+#else
+
+ return(main2(argc,argv));
+
+#endif /* __APPLE__ || MACOSX */
+
+}
+
--- /dev/null
+// Cheapo - a QuakeWorld Proxy
+// Copyright (C) 1998 Sami Tammilehto
+//
+// 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.
+
+#include "proxy.h"
+
+//-------------------------------------------------------
+void rankchange(User *u,int pl,int dooverride)
+{
+ int newcol;
+ int frags,ratio,time;
+ int best,besti,bestratio,besttime;
+ int i;
+
+ if(pl<1 || pl>32) return;
+
+ // find leader
+ best=0;
+ besti=0;
+ for(i=1;i<=32;i++)
+ {
+ if(u->playerfrags[i]>best)
+ {
+ besti=i;
+ best=u->playerfrags[i];
+ }
+ }
+ u->playerfragsbest=best;
+
+ if(!besti)
+ {
+ besttime=0;
+ bestratio=1;
+ }
+ else
+ {
+ i=u->playertime[besti]+u->time;
+ if(i<1000) i=1000;
+ besttime=i;
+ bestratio=1000000*best/i;
+ }
+
+ frags=u->playerfrags[pl];
+
+ i=u->playertime[pl]+u->time;
+ if(i<1000) i=1000;
+ time=i;
+ ratio=1000000*frags/i;
+
+// printf("pl %2i: time %6i frags %3i ratio %6i (best %3i ratio %6i time %6i)\n",
+// pl,i,frags,ratio,best,bestratio,besttime);
+
+ if(frags<=0 || time<60000) newcol=0; // <=0 frags or <=1 min (WHITE)
+ else if(frags>=best || ratio>=bestratio) newcol=4 ; // leader(s) RED
+ else if(ratio>=bestratio*3/4) newcol=12; // ratio over 75% YELLOW
+ else if(ratio>=bestratio*2/4) newcol=13; // ratio over 50% BLUE
+ else if(ratio>=bestratio*1/4) newcol=3 ; // ratio over 25% GREEN
+ else newcol=0 ; // the rest WHITE
+
+ if(newcol!=u->playerrcolor[pl])
+ {
+ u->playerrcolor[pl]=newcol;
+ if(dooverride) skinoverride(u,pl);
+ }
+}
+
+void skinoverride(User *u,int i) // also coloroverride
+{
+ char *skin = NULL;
+ char *color = NULL,*color2 = NULL;
+ char colortext[4];
+ char colortext2[4];
+ //char buf2[32],*p2;
+ //char buf[80],*p;
+
+ skin=NULL;
+
+/*
+ // check and cut long skin names
+ if(strlen(u->playerskin[i])>19)
+ {
+ u->playerskin[i][19]=0;
+ skin=u->playerskin[i];
+ q_printf(u,"DEBUG: %s longskin\n",u->playername[i]);
+ }
+*/
+
+ for(;;)
+ {
+ if(u->fortress) break;
+ if(u->fpd&FPD_SKIN) break;
+ if(!u->skinoverride) break;
+ if(!u->playeractive[i]) break;
+ if(u->time_reskinall) break;
+
+ skin=(SAMETEAM(i))?u->skinname:u->skinname2;
+ if(*skin=='*' || u->skinoverride==-1) skin=u->playerskin[i];
+
+ /*
+ p=u->playerskin[i];
+ p2=buf2;
+ while(*p)
+ {
+ if(*p=='.') break;
+ *p2++=*p++;
+ if(p2>buf2+15) break;
+ }
+ *p2=0;
+ */
+
+ if(u->skinoverride==1)
+ {
+// q_printf(u,"DEBUG: %15s %s\n",u->playername[i],skin);
+ }
+
+ break;
+ }
+
+ if(skin)
+ {
+ setinfo(u,i,"skin",skin);
+ }
+
+ //-------- color ------------
+
+ color=NULL;
+
+ for(;;)
+ {
+ if(u->fortress) break;
+ if(u->fpd&FPD_COLOR) break;
+ if(!u->coloroverride) break;
+ if(!u->playeractive[i]) break;
+ if(u->time_reskinall) break;
+
+ {
+ int c;
+ if(SAMETEAM(i)) c=u->color;
+ else c=u->color2;
+
+ if(c==-3)
+ {
+ color=color2=colortext;
+ if(u->playerrcolor[i]<0) *color='-';
+ else sprintf(colortext,"%i",u->playerrcolor[i]);
+ }
+ else if(c==-2)
+ {
+ color=color2=colortext;
+ if(u->playerxcolor[i]<0) *color='-';
+ else *color='+';
+ }
+ else if(c<0)
+ {
+ color=color2=colortext;
+ *color='-';
+ }
+ else if(c<256)
+ {
+ color=color2=colortext;
+ sprintf(color,"%i",c&15);
+ }
+ else
+ {
+ color=colortext;
+ color2=colortext2;
+ sprintf(colortext,"%i",(c>>4)&15);
+ sprintf(colortext2,"%i",c&15);
+ }
+ }
+
+ if(u->coloroverride==-1 || *color=='-')
+ {
+ color=colortext;
+ color2=colortext2;
+ sprintf(colortext,"%i",u->playertcolor[i]);
+ sprintf(colortext2,"%i",u->playerbcolor[i]);
+ }
+ else if(*color=='+')
+ {
+ color=colortext;
+ color2=colortext2;
+ sprintf(colortext,"%i",(u->playerxcolor[i]>>4)&15);
+ sprintf(colortext2,"%i",(u->playerxcolor[i])&15);
+ }
+
+ break;
+ }
+
+ if(color)
+ {
+ setinfo(u,i,"topcolor",color);
+ setinfo(u,i,"bottomcolor",color2);
+ }
+}
+
+//-------------------------------------------------------
+
+uchar *findnear(User *u,int x,int y,int z,int *ind)
+{
+ int i,ibest=-1;
+ int d,dbest=9999999;
+ for(i=0;i<u->locations;i++)
+ {
+ d=abs(x-u->loc[i].x)+
+ abs(y-u->loc[i].y)+
+ abs(z-u->loc[i].z);
+ if(d<dbest)
+ {
+ dbest=d;
+ ibest=i;
+ }
+ }
+ if(ind) *ind=ibest;
+ if(ibest<0) return("someplace");
+ else return(u->loc[ibest].name);
+}
+
+char *playerlocdirection(User *u,int pl)
+{
+ char *name1,*name2;
+ static char buf[512];
+ int i;
+
+ i=u->playerlastloc[pl];
+ if(i<0 || i>=u->locations) name1="";
+ else name1=u->loc[i].name;
+
+ i=u->playerloc[pl];
+ if(i<0 || i>=u->locations) name2="";
+ else name2=u->loc[i].name;
+
+ if(strcmp(name1,name2))
+ {
+ strcpy(buf,name1);
+ strcat(buf,"-\x8d");
+ strcat(buf,name2);
+ return(buf);
+ }
+ else return(NULL);
+}
+
+// pointing globals
+static float px,py,pz; // player
+static float dx,dy,dz; // lookdir
+static float best;
+static int showdebug;
+
+int pointcheck(User *u,int x,int y,int z,char *name)
+{ // returns 1 if this object should replace last
+ float jx,jy,jz;
+ float kx,ky,kz;
+ float zdist; // depth distance
+ float pdist; // perpendicular distance
+ float dist; // dist used for evaluation
+
+ jx=(float)x-px;
+ jy=(float)y-py;
+ jz=(float)z-pz;
+ zdist=(jx*dx+jy*dy+jz*dz);
+
+ if(zdist<100) return(0);
+
+ kx=dx*zdist-jx;
+ ky=dy*zdist-jy;
+ kz=dz*zdist-jz;
+ pdist=(float)sqrt(kx*kx+ky*ky+kz*kz);
+
+ if(pdist>2000) return(0);
+
+ if(pdist>zdist) return(0); // aim over 45 degrees off
+
+ if(zdist<3000)
+ {
+ dist=pdist*(zdist*0.0002f+0.3f);
+ }
+ else dist=pdist;
+
+ if(showdebug)
+ {
+ char buf[64];
+ sprintf(buf,"- z:%5.0f p:%5.0f d:%5.0f %s\n",zdist,pdist,dist,name);
+ q_print(u,buf);
+ }
+
+ if(dist<best)
+ {
+ best=dist;
+ return(1);
+ }
+ return(0);
+}
+
+uchar *findpoint(User *u,int x,int y,int z,int ax,int ay,int az,int targetmask,int flags,int *entnum)
+{
+ static char buf[256];
+ uchar *name;
+ int namex = 0,namey = 0,namez = 0;
+ int gotplayer=0; // 1=teammate
+ int gotplayernum=0;
+ int i;
+
+ if(entnum) *entnum=0;
+
+ // parameters for pointcheck
+ dx=(float)(cos((double)ay/32768.0*3.141592)* cos((double)ax/32768.0*3.141592));
+ dy=(float)(sin((double)ay/32768.0*3.141592)* cos((double)ax/32768.0*3.141592));
+ dz=(float)( -sin((double)ax/32768.0*3.141592));
+ px=(float)x;
+ py=(float)y;
+ pz=(float)z;
+ best=99999999.0f;
+ showdebug=(targetmask==-1);
+
+ // result clear
+ *buf=0;
+ name=NULL;
+
+ if(flags&POINT_SELF)
+ {
+ gotplayernum=u->entity;
+ gotplayer=1;
+ name="self";
+ namex=u->playerpos[gotplayernum][0];
+ namey=u->playerpos[gotplayernum][1];
+ namez=u->playerpos[gotplayernum][2];
+ }
+ else
+ {
+ if(targetmask&(CAMP_PLAYER|CAMP_ENEMY))
+ { // players
+ for(i=1;i<=32;i++) if(u->playeractive[i] && u->playernear[i] && i!=u->entity)
+ {
+ if(pointcheck(u,u->playerpos[i][0],u->playerpos[i][1],u->playerpos[i][2],"player"))
+ {
+ int lo,hi;
+
+ *buf=0;
+
+ namex=u->playerpos[i][0];
+ namey=u->playerpos[i][1];
+ namez=u->playerpos[i][2];
+
+ lo=u->playerglow[i]&0x0f;
+ hi=u->playerglow[i]&0xf0;
+
+ if(u->playerglow[i]&0x100)
+ {
+ if(hi&0x40)
+ {
+ strcat(buf,"quaded ");
+ }
+ if(hi&0x80)
+ {
+ strcat(buf,"penta ");
+ }
+
+ strcat(buf,"eyes");
+ gotplayer=3;
+ }
+ else
+ {
+ if(hi&0x10) strcat(buf,"red FLAG on ");
+ else if(hi&0x20) strcat(buf,"blue FLAG on ");
+
+ if(hi&0x40)
+ {
+ strcat(buf,"quaded ");
+ }
+ if(hi&0x80)
+ {
+ strcat(buf,"penta ");
+ }
+
+ if(SAMETEAM(i))
+ {
+ strcat(buf,u->playername[i]);
+ gotplayer=1;
+ }
+ else
+ {
+ strcat(buf,"enemy");
+ if((flags&POINT_ADDSKIN) && u->fortress)
+ {
+ char *s;
+ s=u->playerskin[i];
+ if(s[0]=='t' && s[1]=='f' && s[2]=='_')
+ {
+ s+=3;
+ if(!strcmp(s,"demo.pcx")) strcat(buf," demoman");
+ if(!strcmp(s,"eng.pcx")) strcat(buf," engineer");
+ if(!strcmp(s,"hwguy.pcx")) strcat(buf," hw-guy");
+ if(!strcmp(s,"medic.pcx")) strcat(buf," medic");
+ if(!strcmp(s,"pyro.pcx")) strcat(buf," pyro");
+ if(!strcmp(s,"scout.pcx")) strcat(buf," scout");
+ if(!strcmp(s,"snipe.pcx")) strcat(buf," sniper");
+ if(!strcmp(s,"sold.pcx")) strcat(buf," soldier");
+ if(!strcmp(s,"spy.pcx")) strcat(buf," spy");
+ }
+ }
+ gotplayer=2;
+ }
+ }
+
+ if(entnum) *entnum=i;
+
+ name=buf;
+ gotplayernum=i;
+ }
+ }
+ }
+
+ if(gotplayer==1 && !(targetmask&CAMP_PLAYER))
+ { // got own player, and only CAMP_ENEMY set
+ gotplayer=0;
+ name=NULL;
+ best=99999999.0f;
+ }
+
+ if(targetmask&(~(CAMP_PLAYER|CAMP_ENEMY)))
+ {
+ for(i=0;i<MAXENTITIES;i++) if(u->entityvisible[i] && (u->entitymask[i]&targetmask))
+ {
+ if(pointcheck(u,u->entitypos[i][0],u->entitypos[i][1],u->entitypos[i][2],u->entityname[i]))
+ {
+ gotplayer=0;
+ namex=u->entitypos[i][0];
+ namey=u->entitypos[i][1];
+ namez=u->entitypos[i][2];
+ name=u->entityname[i];
+ if(entnum) *entnum=i;
+ }
+ }
+ }
+ }
+
+ if(flags&POINT_TEAMMATENUMBER)
+ {
+ sprintf(buf,"%i",gotplayernum);
+ return(buf);
+ }
+ else if(name==NULL)
+ {
+ // do nothing
+ return("."); // nothing found
+ }
+ else
+ {
+ // add location
+ if(flags&POINT_ONLYNAME)
+ {
+ strcpy(buf,name);
+ }
+ else if(flags&POINT_ONLYPOS)
+ {
+ strcpy(buf,findnear(u,namex,namey,namez,NULL));
+ }
+ else
+ {
+ strcpy(buf,name);
+ strcat(buf," at ");
+ if(gotplayer && (flags&POINT_DIRECTION))
+ {
+ char *p;
+ p=playerlocdirection(u,gotplayernum);
+ if(p) strcat(buf,p);
+ else strcat(buf,findnear(u,namex,namey,namez,NULL));
+ }
+ else
+ {
+ strcat(buf,findnear(u,namex,namey,namez,NULL));
+ }
+ }
+
+ return(buf);
+ }
+}
+
+uchar *findnearestitem(User *u,int x,int y,int z,int targetmask)
+{ // onlynamepos: 1=name, 2=pos
+ static char buf[256];
+ char *name;
+ int namex,namey,namez;
+ int ix,iy,iz,i;
+ float jx,jy,jz;
+ float dist;
+ float namedist=50000.0f;
+#if !defined(__APPLE__) && !defined(MACOSX)
+ int gotplayer=0; // 1=teammate
+ int gotplayernum=0;
+ float distlimit=10000.0f,zdistlimit=3500.0f;
+#endif /* !__APPLE__ && !MACOSX */
+
+ *buf=0;
+ name=NULL;
+ namex=0;
+ namey=0;
+ namez=0;
+
+ for(i=0;i<MAXENTITIES;i++) if(u->entityvisible[i] && (u->entitymask[i]&targetmask))
+ {
+ ix=u->entitypos[i][0]; jx=(float)(ix-x);
+ iy=u->entitypos[i][1]; jy=(float)(iy-y);
+ iz=u->entitypos[i][2]; jz=(float)(iz-z);
+ dist=(float)sqrt(jx*jx+jy*jy+jz*jz);
+
+ if(dist<namedist)
+ {
+ namedist=dist;
+ namex=ix;
+ namey=iy;
+ namez=iz;
+ name=u->entityname[i];
+ }
+ }
+
+ if(name==NULL)
+ {
+ strcpy(buf,".");
+ return(buf);
+ }
+ else
+ {
+ strcat(buf,name);
+ strcat(buf," at ");
+ strcat(buf,findnear(u,namex,namey,namez,NULL));
+
+ return(buf);
+ }
+}
+
+
+//-------------------------------------------------------
+
+#define SHOWINFOMESSAGES
+
+void userinfofromuser(Userinfo *ui,User *u)
+{
+ ui->own=1;
+ ui->name=u->name;
+
+ ui->armor=u->armor;
+ ui->health=u->health;
+
+ if(0) { }
+ else if(u->weapon&1) {
+ ui->weapon=2-1;
+ ui->ammo=u->ammo_shells;
+ }
+ else if(u->weapon&2) {
+ ui->weapon=3-1;
+ ui->ammo=u->ammo_shells;
+ }
+ else if(u->weapon&4) {
+ ui->weapon=4-1;
+ ui->ammo=u->ammo_nails;
+ }
+ else if(u->weapon&8) {
+ ui->weapon=5-1;
+ ui->ammo=u->ammo_nails;
+ }
+ else if(u->weapon&16) {
+ ui->weapon=6-1;
+ ui->ammo=u->ammo_rockets;
+ }
+ else if(u->weapon&32) {
+ ui->weapon=7-1;
+ ui->ammo=u->ammo_rockets;
+ }
+ else if(u->weapon&64) {
+ ui->weapon=8-1;
+ ui->ammo=u->ammo_cells;
+ }
+ else
+ {
+ ui->weapon=1-1;
+ ui->ammo=0;
+ }
+
+ if(0) { }
+ else if((u->items&32) && u->ammo_rockets>=0) {
+ ui->bestweapon=7-1;
+ ui->bestammo=u->ammo_rockets;
+ }
+ else if((u->items&64) && u->ammo_cells>=0) {
+ ui->bestweapon=8-1;
+ ui->bestammo=u->ammo_cells;
+ }
+ else if((u->items&16) && u->ammo_rockets>=0) {
+ ui->bestweapon=6-1;
+ ui->bestammo=u->ammo_rockets;
+ }
+ else if((u->items&8) && u->ammo_nails>=0) {
+ ui->bestweapon=5-1;
+ ui->bestammo=u->ammo_nails;
+ }
+ else if((u->items&4) && u->ammo_nails>=0) {
+ ui->bestweapon=4-1;
+ ui->bestammo=u->ammo_nails;
+ }
+ else if((u->items&2) && u->ammo_shells>=0) {
+ ui->bestweapon=3-1;
+ ui->bestammo=u->ammo_shells;
+ }
+ else if((u->items&1) && u->ammo_shells>=0) {
+ ui->bestweapon=2-1;
+ ui->bestammo=u->ammo_shells;
+ }
+ else
+ {
+ ui->bestweapon=1-1;
+ ui->bestammo=0;
+ }
+
+ ui->items=u->items;
+ ui->power=u->power;
+
+ ui->x=u->pos[0];
+ ui->y=u->pos[1];
+ ui->z=u->pos[2];
+}
+
+void sendsetinfo(User *u,uchar *msg)
+{
+ uchar *p;
+
+ sprintf(tmpbuf,"setinfo %s",msg);
+ u->appendclient[u->appendclientbytes++]=0x04;
+ p=tmpbuf; while(*p) u->appendclient[u->appendclientbytes++]=*p++;
+ u->appendclient[u->appendclientbytes++]=0;
+}
+
+void strxor80(char *t)
+{
+ while(*t)
+ {
+ if((*t&0x7f)>=0x20)
+ {
+ *t^=0x80;
+ }
+ t++;
+ }
+}
+
+void stror80(char *t)
+{
+ while(*t)
+ {
+ if((*t&0x7f)>=0x20)
+ {
+ *t|=0x80;
+ }
+ t++;
+ }
+}
+
+int memcmp7f(char *a,char *b,int len)
+{
+ while(len--)
+ {
+ if((*a&0x7f)!=(*b&0x7f)) return(1);
+ a++;
+ b++;
+ }
+ return(0);
+}
+
+void strand80(char *t)
+{
+ while(*t)
+ {
+ if((*t&0x7f)>=0x20)
+ {
+ *t&=~0x80;
+ }
+ t++;
+ }
+}
+
+int teamint(uchar *a)
+{
+ unsigned int b,x,i;
+ b=0;
+ for(i=0;i<16;i++)
+ {
+ x=*a++;
+ if(!x) return(b);
+
+ b=(b<<8)|(b>>24);
+ b^=x;
+ }
+ return(b);
+}
+
+int is_version(uchar *x)
+{
+ x--;
+ if(x[1]=='_' && x[3]=='e' && x[2]=='v' &&
+ x[4]=='r' && x[5]=='s' && x[6]=='i' && x[7]=='o' && x[8]=='n')
+ {
+ return(1);
+ }
+ else
+ {
+ return(0);
+ }
+}
+
+int is_timer(uchar *x)
+{
+ x--;
+ if(x[1]=='_' && x[3]=='i' && x[2]=='t' &&
+ x[4]=='m' && x[5]=='e' && x[6]=='r')
+ {
+ return(1);
+ }
+ else
+ {
+ return(0);
+ }
+}
+
+int is_modified(uchar *x)
+{
+ x--;
+ if(x[1]=='_' && x[3]=='o' && x[2]=='m' &&
+ x[4]=='d' && x[5]=='i' && x[6]=='f' && x[7]=='i' && x[8]=='e' && x[9]=='d')
+ {
+ return(1);
+ }
+ else
+ {
+ return(0);
+ }
+}
+
+void createversiontext(User *u,uchar *txt)
+{
+ int flags=0;
+#ifdef PROMODE
+ flags|=0x01;
+#endif
+ if(u->nofversion) flags|=2;
+ if(u->campthese) flags|=0x10;
+ if(u->campthese&(~CAMP_POWER)) flags|=0x20;
+ sprintf(txt+4,"c%s (%02X)",version,flags);
+ txt[0]='s';
+ txt[1]='a';
+ txt[2]='y';
+ txt[3]=' ';
+}
+
+int weaponok(User *u,int weapon)
+{
+ switch(weapon)
+ {
+ case 1: return(1);
+ case 2: return( (u->items & 1 ) && u->ammo_shells >=1 );
+ case 3: return( (u->items & 2 ) && u->ammo_shells >=2 );
+ case 4: return( (u->items & 4 ) && u->ammo_nails >=1 );
+ case 5: return( (u->items & 8 ) && u->ammo_nails >=1 );
+ case 6: return( (u->items & 16) && u->ammo_rockets>=1 );
+ case 7: return( (u->items & 32) && u->ammo_rockets>=1 );
+ case 8: return( (u->items & 64) && u->ammo_cells >=1 );
+ default: return(1);
+ }
+}
+
+int modifyimpulse(User *u,int *impulse)
+{
+ int i,i0,seq,new,j;
+ i=i0=*impulse;
+
+ for(seq=0;seq<SEQUENCES;seq++)
+ {
+ if(i0==u->impulseorder[seq][0])
+ {
+ for(j=1;j<14;j++)
+ {
+ new=u->impulseorder[seq][j];
+ if(!new) break;
+ if(weaponok(u,new)) i=new;
+ }
+
+ if(i!=i0)
+ {
+ *impulse=i;
+ return(1);
+ }
+ else return(0);
+ }
+ }
+
+ if(u->bestweapon)
+ {
+ if(i0==4 && (u->items & 4 ) && u->ammo_nails >=1) i=4;
+ else if(i0>8) i=i0;
+ else if(i0==8 && (u->items & 64) && u->ammo_cells >=1) i=8;
+ else if(i0==7 && (u->items & 32) && u->ammo_rockets>=1) i=7;
+ else if(i0==6 && (u->items & 16) && u->ammo_rockets>=1) i=6;
+ else if(i0>=5 && (u->items & 8 ) && u->ammo_nails >=1) i=5;
+ else if(i0>=3 && (u->items & 2 ) && u->ammo_shells >=2) i=3;
+ else if(i0>=4 && (u->items & 4 ) && u->ammo_nails >=1) i=4;
+ else if(i0>=2 && (u->items & 1 ) && u->ammo_shells >=1) i=2;
+ else i=i0; // if axe just use the original impulse
+
+ if(i!=i0)
+ {
+ *impulse=i;
+ return(1);
+ }
+ else return(0);
+ }
+
+ return(0);
+}
+
+void setinfo(User *u,int i,char *attr,char *value)
+{
+ char *p;
+ u->appendserver[u->appendserverbytes++]=0x33;
+ u->appendserver[u->appendserverbytes++]=i-1;
+ p=attr;
+ for(;;)
+ {
+ u->appendserver[u->appendserverbytes++]=*p;
+ if(!*p) break;
+ p++;
+ }
+ p=value;
+ for(;;)
+ {
+ u->appendserver[u->appendserverbytes++]=*p;
+ if(!*p) break;
+ p++;
+ }
+}
+
+//-------------------------------------------------------
+
+void spectatenext(User *u,uchar *findname)
+{ // spectator change
+ int i,j;
+
+ if(findname)
+ {
+ uchar buf1[32],buf2[32];
+ memset(buf1,0,32);
+ memset(buf2,0,32);
+ strncpy(buf1,findname,31);
+ strlwr(buf1);
+
+ for(i=0;i<u->numplayers;i++)
+ {
+ u->specentity++;
+ if(u->specentity>31) u->specentity=1;
+
+ if(u->playeractive[u->specentity] && u->specentity!=u->entity)
+ { // player with this index exists!
+ strncpy(buf2,u->playername[u->specentity],31);
+ for(j=strlen(buf2)-1;j>=0;j--) buf2[j]&=0x7f;
+ strlwr(buf2);
+
+ if(strstr(buf2,buf1))
+ {
+ break;
+ }
+ }
+ }
+ if(i>=33)
+ {
+ u->specentity=0;
+ return;
+ }
+ }
+ else
+ {
+ for(i=0;i<u->numplayers;i++)
+ {
+ u->specentity++;
+
+ if(u->specentity>31) u->specentity=1;
+ if(u->playeractive[u->specentity] && u->specentity!=u->entity)
+ { // player with this index exists!
+ break;
+ }
+ }
+ if(i>=33)
+ {
+ u->specentity=0;
+ sprintf(tmpbuf,"Nobody to track!\n");
+ q_print(u,tmpbuf);
+ return;
+ }
+ }
+
+ sprintf(tmpbuf,"ptrack %i",u->specentity-1);
+ q_servercmd(u,tmpbuf);
+
+ u->time_nextcamp=0; // update text
+}
+
+void cl_clearstats(User *u)
+{
+ u->health=100;
+ u->armor=0;
+ u->ammo=0;
+ u->ammo_shells=0;
+ u->ammo_nails=0;
+ u->ammo_rockets=0;
+ u->ammo_cells=0;
+ u->weapon=1;
+ u->items=1;
+}
+
+void cl_setinfo(User *u,uchar *attr,uchar *value)
+{
+ uchar *p,*s2,*d2;
+ int found=0;
+ static char tmpbuf[CLIENTINFOSIZE];
+
+ logx(LOG_DEBUG,"setinfo %s to %s\n",attr,value);
+
+ if(!strcmp(attr,"name"))
+ {
+ uchar buf[80];
+ if(strcmp(u->name,value))
+ {
+ sprintf(buf,"%s is now %s\n",u->name,value);
+ strncpy(u->name,value,MAXSTR-1);
+ q_print(u,buf);
+ }
+ }
+ if(!strcmp(attr,"team"))
+ {
+ u->team=teamint(value);
+ }
+ if(!strcmp(attr,"topcolor"))
+ {
+ u->tcolor=atoi(value);
+ }
+ if(!strcmp(attr,"bottomcolor"))
+ {
+ u->bcolor=atoi(value);
+ }
+
+ memcpy(tmpbuf,u->clientinfo,CLIENTINFOSIZE);
+ d2=u->clientinfo;
+ s2=tmpbuf;
+ if(*s2) *d2++=*s2++;
+ if(*s2) *d2++=*s2++;
+ while(*s2)
+ {
+// logx(LOG_DEBUG,"Compare <%s> to %s",attr,s2);
+ if(*s2=='\"')
+ { // end of list
+ if(d2[-1]=='\\') d2--;
+ if(!found && *value)
+ {
+ sprintf(d2,"\\%s\\%s\"\n",attr,value);
+ d2+=strlen(d2);
+ }
+ else
+ {
+ *d2++='"';
+ *d2++='\n';
+ *d2++=0;
+ }
+ break;
+ }
+ else if(!memicmp(attr,s2,strlen(attr)))
+ { // matching attribute
+ if(*value==0)
+ { // remove
+ // skip name
+ while(*s2!='\\' && *s2) s2++;
+ // skip slash
+ if(*s2=='\\') s2++;
+ // skip value
+ while(*s2!='\\' && *s2!='\"' && *s2) s2++;
+ // skip slash
+ if(*s2=='\\') s2++;
+ }
+ else
+ { // change
+ // copy name
+ while(*s2!='\\' && *s2) *d2++=*s2++;
+ // copy slash
+ if(*s2=='\\') *d2++=*s2++;
+
+ // skip original value
+ while(*s2!='\\' && *s2!='\"' && *s2) s2++;
+ // insert new value
+ p=value;
+ while(*p) *d2++=*p++;
+ // copy slash
+ if(*s2=='\\') *d2++=*s2++;
+ }
+
+ found=1;
+ }
+ else
+ { // other attribute, just copy
+ while(*s2!='\\' && *s2) *d2++=*s2++;
+ if(*s2=='\\') *d2++=*s2++;
+ while(*s2!='\\' && *s2!='\"' && *s2) *d2++=*s2++;
+ if(*s2=='\\') *d2++=*s2++;
+ }
+ }
+
+ logx(LOG_DEBUG,"clientinfo=%s",u->clientinfo);
+}
+
+Camp *campadd(User *u,int entity,int *pos,int modelindex,int skin)
+{
+ Camp *ca;
+
+ ca=&u->camp[u->camps];
+ ca->entity=entity;
+ ca->pos[0]=pos[0];
+ ca->pos[1]=pos[1];
+ ca->pos[2]=pos[2];
+
+ if(strstr(u->model[modelindex],"armor"))
+ {
+ if(skin==2) ca->name[0]='r';
+ else if(skin==1) ca->name[0]='y';
+ else ca->name[0]='g';
+ ca->name[1]='a';
+ ca->name[2]='0'+(u->campcnt[15-skin]++);
+ ca->name[3]=0;
+ ca->delay=20000;
+ ca->pickupsound="armor1.wav";
+ if(skin==2) ca->category=CAMP_RA;
+ else if(skin==1) ca->category=CAMP_YA;
+ else ca->category=CAMP_GA;
+ ca->genericname[0]=ca->name[0];
+ ca->genericname[1]=ca->name[1];
+ ca->genericname[2]=0;
+ }
+ else if(strstr(u->model[modelindex],"b_bh100"))
+ {
+ ca->name[0]='m';
+ ca->name[1]='h';
+ ca->name[2]='0'+(u->campcnt[1]++);
+ ca->name[3]=0;
+ ca->delay=125000;
+ ca->category=CAMP_MH;
+ ca->pickupsound="r_item2.wav";
+ ca->genericname[0]='m';
+ ca->genericname[1]='e';
+ ca->genericname[2]='g';
+ ca->genericname[3]='a';
+ ca->genericname[4]=0;
+ }
+ else if(strstr(u->model[modelindex],"suit"))
+ {
+ ca->name[0]='s';
+ ca->name[1]='u';
+ ca->name[2]='0'+(u->campcnt[2]++);
+ ca->name[3]=0;
+ ca->delay=125000;
+ ca->category=CAMP_SUIT;
+ ca->pickupsound="?.wav";
+ ca->genericname[0]='s';
+ ca->genericname[1]='u';
+ ca->genericname[2]='i';
+ ca->genericname[3]='t';
+ ca->genericname[4]=0;
+ }
+ else if(strstr(u->model[modelindex],"invisib"))
+ {
+ ca->name[0]='r';
+ ca->name[1]='0'+(u->campcnt[3]++);
+ ca->name[2]=0;
+ ca->delay=5*60000;
+ ca->category=CAMP_R;
+ ca->pickupsound="inv1.wav";
+ ca->genericname[0]='r';
+ ca->genericname[1]='i';
+ ca->genericname[2]='n';
+ ca->genericname[3]='g';
+ ca->genericname[4]=0;
+ }
+ else if(strstr(u->model[modelindex],"invulne"))
+ {
+ ca->name[0]='p';
+ ca->name[1]='0'+(u->campcnt[4]++);
+ ca->name[2]=0;
+ ca->delay=5*60000;
+ ca->category=CAMP_P;
+ ca->pickupsound="protect.wav";
+ ca->genericname[0]='p';
+ ca->genericname[1]='e';
+ ca->genericname[2]='n';
+ ca->genericname[3]='t';
+ ca->genericname[4]=0;
+ }
+ else if(strstr(u->model[modelindex],"quaddama"))
+ {
+ ca->name[0]='q';
+ ca->name[1]='0'+(u->campcnt[5]++);
+ ca->name[2]=0;
+ ca->delay=60000;
+ ca->category=CAMP_Q;
+ ca->pickupsound="damage.wav";
+ ca->genericname[0]='q';
+ ca->genericname[1]='u';
+ ca->genericname[2]='a';
+ ca->genericname[3]='d';
+ ca->genericname[4]=0;
+ }
+ else if(strstr(u->model[modelindex],"g_rock2"))
+ {
+ ca->name[0]='r';
+ ca->name[1]='l';
+ ca->name[2]='0'+(u->campcnt[6]++);
+ ca->name[3]=0;
+ ca->delay=30000;
+ ca->category=CAMP_RL;
+ ca->pickupsound="pkup.wav";
+ ca->genericname[0]=ca->name[0];
+ ca->genericname[1]=ca->name[1];
+ ca->genericname[2]=0;
+ }
+ else if(strstr(u->model[modelindex],"g_rock"))
+ {
+ ca->name[0]='g';
+ ca->name[1]='l';
+ ca->name[2]='0'+(u->campcnt[7]++);
+ ca->name[3]=0;
+ ca->delay=30000;
+ ca->category=CAMP_GL;
+ ca->pickupsound="pkup.wav";
+ ca->genericname[0]=ca->name[0];
+ ca->genericname[1]=ca->name[1];
+ ca->genericname[2]=0;
+ }
+ else if(strstr(u->model[modelindex],"g_light"))
+ {
+ ca->name[0]='l';
+ ca->name[1]='g';
+ ca->name[2]='0'+(u->campcnt[8]++);
+ ca->name[3]=0;
+ ca->delay=30000;
+ ca->category=CAMP_LG;
+ ca->pickupsound="pkup.wav";
+ ca->genericname[0]=ca->name[0];
+ ca->genericname[1]=ca->name[1];
+ ca->genericname[2]=0;
+ }
+#if 0
+ else if(strstr(u->model[modelindex],"g_nail2"))
+ {
+ ca->name[0]='s';
+ ca->name[1]='n';
+ ca->name[2]='0'+(u->campcnt[9]++);
+ ca->name[3]=0;
+ ca->delay=30000;
+ ca->category=CAMP_SNG;
+ ca->pickupsound="pkup.wav";
+ ca->genericname[0]='s';
+ ca->genericname[1]='n';
+ ca->genericname[2]='g';
+ ca->genericname[3]=0;
+ }
+ else if(strstr(u->model[modelindex],"g_nail"))
+ {
+ ca->name[0]='n';
+ ca->name[1]='g';
+ ca->name[2]='0'+(u->campcnt[10]++);
+ ca->name[3]=0;
+ ca->delay=30000;
+ ca->category=CAMP_NG;
+ ca->pickupsound="pkup.wav";
+ ca->genericname[0]='n';
+ ca->genericname[1]='g';
+ ca->genericname[2]=0;
+ }
+ else if(strstr(u->model[modelindex],"g_shot"))
+ {
+ ca->name[0]='s';
+ ca->name[1]='s';
+ ca->name[2]='0'+(u->campcnt[12]++);
+ ca->name[3]=0;
+ ca->delay=30000;
+ ca->category=CAMP_SSG;
+ ca->pickupsound="pkup.wav";
+ ca->genericname[0]='s';
+ ca->genericname[1]='s';
+ ca->genericname[2]='g';
+ ca->genericname[3]=0;
+ }
+#endif
+ else if(strstr(u->model[modelindex],"flag") || strstr(u->model[modelindex],"tf_stan"))
+ {
+ int color='?';
+ // find color
+ if(strstr(u->model[modelindex],"tf_"))
+ {
+ // in tf:
+ // 0=?
+ // 1=blue
+ // 2=red
+ // 3,4=black?,yellow?
+ if(skin==1) color='b';
+ else if(skin==2) color='r';
+ }
+ else //if(u->ctf)
+ {
+ // in ctf:
+ // 0=red
+ // 1=blue
+ if(skin==1) color='b';
+ else if(skin==0) color='r';
+ }
+
+ ca->name[0]='!';
+ ca->name[1]=ca->name[2]=color;
+ ca->name[2]=0;
+ ca->name[3]=0;
+ ca->delay=30000;
+ ca->category=CAMP_RUNE;
+ ca->pickupsound="pkup.wav";
+ ca->genericname[0]='f';
+ ca->genericname[1]='l';
+ ca->genericname[2]='a';
+ ca->genericname[3]='g';
+ ca->genericname[4]=0;
+ }
+ else ca=NULL;
+
+ return(ca);
+}
+
+char *makeentityname(User *u,int modelindex,int skin,int *mask)
+{
+ if(strstr(u->model[modelindex],"backpack"))
+ {
+ *mask=CAMP_PACK;
+ return("pack");
+ }
+ else if(strstr(u->model[modelindex],"_key"))
+ { // also b_ key
+ *mask=CAMP_RUNE;
+ return("key");
+ }
+ else if(strstr(u->model[modelindex],"g_nail2"))
+ {
+ *mask=CAMP_SNG;
+ return("sng");
+ }
+ else if(strstr(u->model[modelindex],"g_nail"))
+ {
+ *mask=CAMP_NG;
+ return("ng");
+ }
+ else if(strstr(u->model[modelindex],"g_shot"))
+ {
+ *mask=CAMP_SSG;
+ return("ssg");
+ }
+ else if(strstr(u->model[modelindex],"b_"))
+ {
+ if(strstr(u->model[modelindex],"b_bh"))
+ {
+ *mask=CAMP_HEALTH;
+ return("health");
+ }
+ else if(strstr(u->model[modelindex],"b_nail"))
+ {
+ *mask=CAMP_NAILS;
+ return("nails");
+ }
+ else if(strstr(u->model[modelindex],"b_rock"))
+ {
+ *mask=CAMP_ROCKETS;
+ return("rockets");
+ }
+ else if(strstr(u->model[modelindex],"b_shell"))
+ {
+ *mask=CAMP_SHELLS;
+ return("shells");
+ }
+ else if(strstr(u->model[modelindex],"b_batt"))
+ {
+ *mask=CAMP_CELLS;
+ return("cells");
+ }
+ }
+ else if(strstr(u->model[modelindex],"tf_flag") || strstr(u->model[modelindex],"tf_stan"))
+ {
+ *mask=CAMP_RUNE;
+ // in tf:
+ // 0=?
+ // 1=blue
+ // 2=red
+ // 3,4=black?,yellow?
+ if(skin==1) return("blue flag");
+ else if(skin==2) return("red flag");
+ else return("flag");
+ }
+ else if(strstr(u->model[modelindex],"flag"))
+ { // after TF_FLAG has been checked
+ *mask=CAMP_RUNE;
+ // in ctf:
+ // 0=red
+ // 1=blue
+ if(skin==1) return("blue flag");
+ else if(skin==0) return("red flag");
+ else return("flag");
+ }
+ else if(strstr(u->model[modelindex],"detpack"))
+ {
+ *mask=CAMP_RUNE;
+ return("detpack");
+ }
+ else if(strstr(u->model[modelindex],"turrgun"))
+ {
+ *mask=CAMP_RUNE;
+ return("sentrygun");
+ }
+ else if(strstr(u->model[modelindex],"disp"))
+ {
+ *mask=CAMP_RUNE;
+ return("dispenser");
+ }
+ else if(strstr(u->model[modelindex],"turrbase"))
+ {
+ *mask=CAMP_RUNE;
+ return("sentrygun");
+ }
+ else if(strstr(u->model[modelindex],"end"))
+ {
+ if(strstr(u->model[modelindex],"end1"))
+ {
+ *mask=CAMP_RUNE;
+ return("resist");
+ }
+ if(strstr(u->model[modelindex],"end2"))
+ {
+ *mask=CAMP_RUNE;
+ return("strength");
+ }
+ if(strstr(u->model[modelindex],"end3"))
+ {
+ *mask=CAMP_RUNE;
+ return("haste");
+ }
+ if(strstr(u->model[modelindex],"end4"))
+ {
+ *mask=CAMP_RUNE;
+ return("regen");
+ }
+ }
+ *mask=0;
+ return("item");
+}
+
+void reportfpd(User *u,int fpd)
+{
+ int f;
+
+ f=litemode;
+ if(f)
+ {
+ q_printf(u,"proxy: lite mode disables:\n");
+ if(f&1) q_print(u,"proxy: - all % reporting\n");
+ if(f&2) q_print(u,"proxy: - poweruptimers\n");
+ if(f&4) q_print(u,"proxy: - soundtrigger\n");
+ if(f&8) q_print(u,"proxy: - (lagfeature)\n");
+ if(f&16) q_print(u,"proxy: - (report lagchange)\n");
+ if(f&32) q_print(u,"proxy: - %e reporting\n");
+ if(f&64) q_print(u,"proxy: - (spectator/player talk)\n");
+ if(f&128) q_print(u,"proxy: - hide pointing (%txyjk)\n");
+ if(f&256) q_print(u,"proxy: - skin forcing\n");
+ if(f&512) q_print(u,"proxy: - color forcing\n");
+ }
+
+ f=fpd&~litemode;
+ if(f)
+ {
+ if(litemode) q_printf(u,"proxy: server disables (fpd=%i):\n",f);
+ else q_printf(u,"proxy: server disables (fpd=%i):\n",f);
+ if(f&1) q_print(u,"proxy: - all % reporting\n");
+ if(u->notimers) q_print(u,"proxy: - poweruptimers (.notimers)\n");
+ else if(f&2) q_print(u,"proxy: - poweruptimers\n");
+ if(f&4) q_print(u,"proxy: - soundtrigger\n");
+ if(f&8) q_print(u,"proxy: - (lagfeature)\n");
+ if(f&16) q_print(u,"proxy: - (report lagchange)\n");
+ if(f&32) q_print(u,"proxy: - %e reporting\n");
+ if(f&64) q_print(u,"proxy: - (spectator/player talk)\n");
+ if(f&128) q_print(u,"proxy: - hide pointing (%txyjk)\n");
+ if(f&256) q_print(u,"proxy: - skin forcing\n");
+ if(f&512) q_print(u,"proxy: - color forcing\n");
+ }
+ else q_print(u,"proxy: no features disabled\n");
+}
+
+//------------------------------
+
+void q_servercmd(User *u,uchar *text)
+{
+ uchar *p;
+ p=u->appendclient+u->appendclientbytes;
+ *p++=0x4;
+ while(*text)
+ {
+ *p++=*text++;
+ }
+ *p++=0;
+ u->appendclientbytes=p-u->appendclient;
+}
+
+void q_explode(User *u,int type,int x,int y,int z)
+{
+ u->appendserver[u->appendserverbytes++]=0x17;
+ u->appendserver[u->appendserverbytes++]=type;
+ if(type==12 || type==2) u->appendserver[u->appendserverbytes++]=16; // 10 particles
+ u->appendserver[u->appendserverbytes++]=x;
+ u->appendserver[u->appendserverbytes++]=x>>8;
+ u->appendserver[u->appendserverbytes++]=y;
+ u->appendserver[u->appendserverbytes++]=y>>8;
+ u->appendserver[u->appendserverbytes++]=z;
+ u->appendserver[u->appendserverbytes++]=z>>8;
+}
+
+void q_print1(User *u,uchar *text)
+{ // print text in white
+ uchar *p;
+ if(u->quiet) return;
+ p=u->appendserver+u->appendserverbytes;
+ *p++=0x8;
+ *p++=2;
+ while(*text)
+ {
+ *p++=*text++;
+ }
+ *p++=0;
+ u->appendserverbytes=p-u->appendserver;
+}
+
+void q_print(User *u,uchar *text)
+{ // print text in red
+ uchar *p;
+ int a;
+ if(u->quiet) return;
+ p=u->appendserver+u->appendserverbytes;
+ *p++=0x8;
+ *p++=2;
+ while(*text)
+ {
+ a=*text++;
+ if((a&0x7f)>32) a^=0x80;
+ *p++=a;
+ }
+ *p++=0;
+ u->appendserverbytes=p-u->appendserver;
+}
+
+void q_printf(User *u,uchar *text,...)
+{ // print text in red
+ static uchar buf[BUFSIZE];
+ va_list argp;
+ va_start(argp,text);
+ vsprintf(buf,text,argp);
+ q_print(u,buf);
+}
+
+void q_centerprint(User *u,uchar *text)
+{
+ uchar *p;
+ p=u->appendserver+u->appendserverbytes;
+ *p++=0x1a;
+ while(*text)
+ {
+ *p++=*text++;
+ }
+ *p++=0;
+ u->appendserverbytes=p-u->appendserver;
+}
+
+void q_stufftext(User *u,uchar *text)
+{
+ uchar *p;
+ p=u->appendserver+u->appendserverbytes;
+ *p++=0x9;
+ while(*text)
+ {
+ *p++=*text++;
+ }
+ *p++=0;
+ u->appendserverbytes=p-u->appendserver;
+}
+
+//------------------------------
+
+void q_growlocmax(User *u,int num)
+{ // num<0 frees
+ Location *old;
+ if(num<u->locationsmax) return;
+ if(num<u->locationsmax+256) num=u->locationsmax+256;
+
+ old=u->loc;
+
+ u->loc=mymalloc(num*sizeof(Location));
+ memset(u->loc,0,num*sizeof(Location));
+ memcpy(u->loc,old,u->locations*sizeof(Location));
+ myfree(old);
+
+ logx(LOG_DEBUG,"max locations grown to %i\n",num);
+ u->locationsmax=num;
+}
+
+void q_setlocname(User *u,int i,char *name)
+{
+ Location *loc;
+ int len;
+
+ while(i>=u->locationsmax-16)
+ {
+ q_growlocmax(u,u->locationsmax*2);
+ }
+
+ loc=u->loc+i;
+
+ if(loc->name)
+ {
+ myfree(loc->name);
+ }
+
+ if(name==NULL)
+ {
+ loc->name=NULL;
+ }
+ else
+ {
+ len=strlen(name);
+ loc->name=mymalloc(len+1);
+ strcpy(loc->name,name);
+ }
+}
+
+void q_loadloc(User *u,uchar *mapname)
+{
+ FILE *f1;
+ uchar tmp[MAXSTR];
+ if(mapname==NULL)
+ {
+ strcpy(tmp,u->locname);
+ }
+ else
+ {
+ strcpy(tmp,mapname);
+ strcat(tmp,".loc");
+ strcpy(u->locname,tmp);
+ }
+ f1=fopen(tmp,"rb");
+ u->locations=0;
+ if(f1)
+ {
+ while(!feof(f1))
+ {
+ *tmp=0;
+ fgets(tmp,255,f1);
+ if(*tmp>32)
+ {
+ uchar *p,*p2;
+ p=tmp;
+
+ while(*p && *p<=32) p++;
+ u->loc[u->locations].x=atoi(p);
+ while(*p && *p>32) p++;
+
+ while(*p && *p<=32) p++;
+ u->loc[u->locations].y=atoi(p);
+ while(*p && *p>32) p++;
+
+ while(*p && *p<=32) p++;
+ u->loc[u->locations].z=atoi(p);
+ while(*p && *p>32) p++;
+
+ while(*p && *p<=32) p++;
+
+ p2=p;
+ while(*p2 && *p2!='\n' && *p2!='\r') p2++;
+ *p2=0;
+
+ q_setlocname(u,u->locations,p);
+
+ logx(LOG_DEBUG,"Location %s (%i,%i,%i)\n",
+ u->loc[u->locations].name,
+ u->loc[u->locations].x,
+ u->loc[u->locations].y,
+ u->loc[u->locations].z);
+
+ u->locations++;
+ if(u->locations>=u->locationsmax-16)
+ {
+ q_growlocmax(u,u->locationsmax*2);
+ }
+ }
+ }
+ fclose(f1);
+ if(u->locations>0)
+ {
+ sprintf(tmp,"proxy: %i locations from %s\n",u->locations,u->locname);
+ q_print(u,tmp);
+ }
+ }
+}
+
+void q_savelocs(User *u)
+{
+ FILE *f1;
+ int i=0;
+ f1=fopen(u->locname,"wb");
+ if(f1)
+ {
+ while(i<u->locations)
+ {
+ fprintf(f1,"%i %i %i %s\n",
+ u->loc[i].x,
+ u->loc[i].y,
+ u->loc[i].z,
+ u->loc[i].name);
+ i++;
+ }
+ fclose(f1);
+ }
+}
+
+//------------------------------
+
+uchar *formatqstring(uchar *in)
+{
+ static uchar buf[MAXSTR];
+ uchar *p=buf;
+ while(*in && p<buf+MAXSTR/2)
+ {
+ if(*in=='\n')
+ {
+ //*p++='\\';
+ //*p++='n';
+ }
+ else *p++=*in;
+ in++;
+ }
+ *p++=0;
+ return(buf);
+}
+
+uchar *extractfield(uchar *dest,uchar *src,uchar *field)
+{
+ uchar *p=src,*p0;
+ uchar *dend=dest+MAXSTR-2;
+ int fieldlen=strlen(field);
+ // skip first /
+ while(*p!='\\' && *p) p++; // skip end of value
+ if(*p) p++;
+ // find key
+ while(*p)
+ {
+ if(!memcmp(p,field,fieldlen))
+ {
+ p+=fieldlen;
+ p0=p;
+ while(*p && *p!='\\' && *p!='"' && dest<dend)
+ {
+ *dest++=*p++;
+ }
+ *dest=0;
+ return(p0);
+ }
+ while(*p!='\\' && *p) p++; // skip end of key
+ if(*p) p++;
+ while(*p!='\\' && *p) p++; // skip end of value
+ if(*p) p++;
+ }
+ *dest=0;
+ return(p);
+}
+
+void saveparseerror(char *text,int pos,uchar *buf,int bytes)
+{
+ FILE *f1;
+ int i;
+ f1=fopen("parse.log","ab");
+ if(f1)
+ {
+ fprintf(f1,text,buf[pos],pos);
+ fwrite(buf,1,bytes,f1);
+ for(i=0;i<16;i++) putc(0xff,f1);
+ for(i=bytes&15;i<16;i++) putc(0x00,f1);
+ fclose(f1);
+ }
+}
+
+//------------------------------
+
+uchar funny[64]=
+//0123456789ABCDEF0123456789ABCDEF
+//aaaaaaaabbbbbbbbccccccccdddddddd
+//01234567012345670123456701234567
+ "(=)a ghij_b c []0123456789.<->"
+ " , ";
+int funny2[64]={
+0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
+0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
+0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
+0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
+0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f};
+
+void funnyhelp(User *u)
+{
+ int i,n=0;
+ uchar *p;
+ q_print(u,"Funny names helper:\n");
+ q_print(u,"names and messages containg\n"
+ "following character sequences\n"
+ "are converted.\n");
+
+ strcpy(tmpbuf,"#X:red version of X\n");
+ tmpbuf[0]^=0x80;
+ tmpbuf[1]^=0x80;
+ q_print(u,tmpbuf);
+
+ strcpy(tmpbuf,"$r:cr, $n:lf (team only)\n");
+ q_print(u,tmpbuf);
+
+ strcpy(tmpbuf,"$0..9:yellow number\n");
+ tmpbuf[0]^=0x80;
+ tmpbuf[1]^=0x80;
+ tmpbuf[4]^=0x80;
+ q_print(u,tmpbuf);
+ p=tmpbuf;
+ for(i=0;i<64;i++) if((funny[i]<'0' || funny[i]>'9') && funny[i]< 'A' && funny[i]!=' ')
+ {
+ sprintf(p,"$%c:%c ",funny[i],funny2[i]);
+ p[0]^=0x80;
+ p[1]^=0x80;
+ p+=strlen(p);
+ n++;
+ if(n>=7)
+ {
+ *p++='\n';
+ n=0;
+ }
+ }
+ for(i=0;i<64;i++) if((funny[i]<'0' || funny[i]>'9') && funny[i]>='A' && funny[i]!=' ')
+ {
+ sprintf(p,"$%c:%c ",funny[i],funny2[i]);
+ p[0]^=0x80;
+ p[1]^=0x80;
+ p+=strlen(p);
+ n++;
+ if(n>=7)
+ {
+ *p++='\n';
+ n=0;
+ }
+ }
+ *p++='\n';
+ n=0;
+ for(i=0;i<32;i++)
+ {
+ sprintf(p,"$x%02X:%c ",i+0x80,i+0x80);
+ p[0]^=0x80;
+ p[1]^=0x80;
+ p[2]^=0x80;
+ p[3]^=0x80;
+ p+=strlen(p);
+ n++;
+ if(n>=5)
+ {
+ *p++='\n';
+ n=0;
+ }
+ }
+ *p++='\n';
+ *p++=0;
+ q_print(u,tmpbuf);
+}
+
+int funnyconvert(uchar *d,uchar *s,int nobits)
+{
+ int chg=0;
+ int nocolor=(nobits&1);
+ int noenter=(nobits&2);
+ while(*s)
+ { // ##, $$
+ if(*s=='#' && !nocolor)
+ {
+ s++;
+ if(*s=='#')
+ {
+ *d++='#';
+ s++;
+ }
+ else if(!*s) *d++=' ';
+ else *d++=*s++^0x80;
+ chg=1;
+ }
+ else if(*s=='$')
+ {
+ int j,x;
+ s++;
+ if(*s=='$')
+ {
+ *d++='$';
+ s++;
+ }
+ else if(*s=='\\' && !noenter)
+ {
+ *d++='x';
+ *d++='\r';
+ s++;
+ }
+ else if(*s=='r' && !noenter)
+ {
+ *d++='\r';
+ s++;
+ }
+ else if(*s=='n' && !noenter)
+ {
+ *d++='\n';
+ s++;
+ }
+ else if(*s=='t' && !noenter)
+ {
+ *d++='\t';
+ s++;
+ }
+#ifdef PROMODE
+ else if(*s=='e')
+ {
+ *d++=0x7f;
+ s++;
+ }
+ else if(*s=='z')
+ {
+ *d++=0x80;
+ s++;
+ }
+#endif
+ else if(*s=='x' && !noenter)
+ {
+ int a=0;
+ s++;
+ sscanf(s,"%02X",&a);
+ *d++=a;
+ if(*s) s++;
+ if(*s) s++;
+ }
+ else if(*s=='\'')
+ {
+ *d++='\"';
+ s++;
+ }
+ else if(!*s) *d++=' ';
+ else
+ {
+ x=tolower(*s);
+ for(j=0;j<64;j++) if(funny[j]==x)
+ {
+ *d++=funny2[j];
+ break;
+ }
+ if(j==64)
+ {
+ *d++='$';
+ *d++=*s;
+ }
+ s++;
+ }
+ chg=1;
+ }
+ else
+ {
+ *d++=*s++;
+ }
+ }
+ *d++=0;
+ return(chg);
+}
+
+//-------------------------------------------------------
+
+unsigned char inittable[1024]={
+0x78,0xD2,0x94,0xE3,0x41,0xEC,0xD6,0xD5,0xCB,0xFC,0xDB,0x8A,0x4B,0xCC,0x85,0x01,
+0x23,0xD2,0xE5,0xF2,0x29,0xA7,0x45,0x94,0x4A,0x62,0xE3,0xA5,0x6F,0x3F,0xE1,0x7A,
+0x64,0xED,0x5C,0x99,0x29,0x87,0xA8,0x78,0x59,0x0D,0xAA,0x0F,0x25,0x0A,0x5C,0x58,
+0xFB,0x00,0xA7,0xA8,0x8A,0x1D,0x86,0x80,0xC5,0x1F,0xD2,0x28,0x69,0x71,0x58,0xC3,
+0x51,0x90,0xE1,0xF8,0x6A,0xF3,0x8F,0xB0,0x68,0xDF,0x95,0x40,0x5C,0xE4,0x24,0x6B,
+0x29,0x19,0x71,0x3F,0x42,0x63,0x6C,0x48,0xE7,0xAD,0xA8,0x4B,0x91,0x8F,0x42,0x36,
+0x34,0xE7,0x32,0x55,0x59,0x2D,0x36,0x38,0x38,0x59,0x9B,0x08,0x16,0x4D,0x8D,0xF8,
+0x0A,0xA4,0x52,0x01,0xBB,0x52,0xA9,0xFD,0x40,0x18,0x97,0x37,0xFF,0xC9,0x82,0x27,
+0xB2,0x64,0x60,0xCE,0x00,0xD9,0x04,0xF0,0x9E,0x99,0xBD,0xCE,0x8F,0x90,0x4A,0xDD,
+0xE1,0xEC,0x19,0x14,0xB1,0xFB,0xCA,0x1E,0x98,0x0F,0xD4,0xCB,0x80,0xD6,0x05,0x63,
+0xFD,0xA0,0x74,0xA6,0x86,0xF6,0x19,0x98,0x76,0x27,0x68,0xF7,0xE9,0x09,0x9A,0xF2,
+0x2E,0x42,0xE1,0xBE,0x64,0x48,0x2A,0x74,0x30,0xBB,0x07,0xCC,0x1F,0xD4,0x91,0x9D,
+0xAC,0x55,0x53,0x25,0xB9,0x64,0xF7,0x58,0x4C,0x34,0x16,0xBC,0xF6,0x12,0x2B,0x65,
+0x68,0x25,0x2E,0x29,0x1F,0xBB,0xB9,0xEE,0x6D,0x0C,0x8E,0xBB,0xD2,0x5F,0x1D,0x8F,
+0xC1,0x39,0xF9,0x8D,0xC0,0x39,0x75,0xCF,0x25,0x17,0xBE,0x96,0xAF,0x98,0x9F,0x5F,
+0x65,0x15,0xC4,0x62,0xF8,0x55,0xFC,0xAB,0x54,0xCF,0xDC,0x14,0x06,0xC8,0xFC,0x42,
+0xD3,0xF0,0xAD,0x10,0x08,0xCD,0xD4,0x11,0xBB,0xCA,0x67,0xC6,0x48,0x5F,0x9D,0x59,
+0xE3,0xE8,0x53,0x67,0x27,0x2D,0x34,0x9E,0x9E,0x24,0x29,0xDB,0x69,0x99,0x86,0xF9,
+0x20,0xB5,0xBB,0x5B,0xB0,0xF9,0xC3,0x67,0xAD,0x1C,0x9C,0xF7,0xCC,0xEF,0xCE,0x69,
+0xE0,0x26,0x8F,0x79,0xBD,0xCA,0x10,0x17,0xDA,0xA9,0x88,0x57,0x9B,0x15,0x24,0xBA,
+0x84,0xD0,0xEB,0x4D,0x14,0xF5,0xFC,0xE6,0x51,0x6C,0x6F,0x64,0x6B,0x73,0xEC,0x85,
+0xF1,0x6F,0xE1,0x67,0x25,0x10,0x77,0x32,0x9E,0x85,0x6E,0x69,0xB1,0x83,0x00,0xE4,
+0x13,0xA4,0x45,0x34,0x3B,0x40,0xFF,0x41,0x82,0x89,0x79,0x57,0xFD,0xD2,0x8E,0xE8,
+0xFC,0x1D,0x19,0x21,0x12,0x00,0xD7,0x66,0xE5,0xC7,0x10,0x1D,0xCB,0x75,0xE8,0xFA,
+0xB6,0xEE,0x7B,0x2F,0x1A,0x25,0x24,0xB9,0x9F,0x1D,0x78,0xFB,0x84,0xD0,0x17,0x05,
+0x71,0xB3,0xC8,0x18,0xFF,0x62,0xEE,0xED,0x53,0xAB,0x78,0xD3,0x65,0x2D,0xBB,0xC7,
+0xC1,0xE7,0x70,0xA2,0x43,0x2C,0x7C,0xC7,0x16,0x04,0xD2,0x45,0xD5,0x6B,0x6C,0x7A,
+0x5E,0xA1,0x50,0x2E,0x31,0x5B,0xCC,0xE8,0x65,0x8B,0x16,0x85,0xBF,0x82,0x83,0xFB,
+0xDE,0x9F,0x36,0x48,0x32,0x79,0xD6,0x9B,0xFB,0x52,0x45,0xBF,0x43,0xF7,0x0B,0x0B,
+0x19,0x19,0x31,0xC3,0x85,0xEC,0x1D,0x8C,0x20,0xF0,0x3A,0xFA,0x80,0x4D,0x2C,0x7D,
+0xAC,0x60,0x09,0xC0,0x40,0xEE,0xB9,0xEB,0x13,0x5B,0xE8,0x2B,0xB1,0x20,0xF0,0xCE,
+0x4C,0xBD,0xC6,0x04,0x86,0x70,0xC6,0x33,0xC3,0x15,0x0F,0x65,0x19,0xFD,0xC2,0xD3};
+
+unsigned int checktable[256]={
+0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,
+0x8108,0x9129,0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF,
+0x1231,0x0210,0x3273,0x2252,0x52B5,0x4294,0x72F7,0x62D6,
+0x9339,0x8318,0xB37B,0xA35A,0xD3BD,0xC39C,0xF3FF,0xE3DE,
+0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4,0x5485,
+0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D,
+0x3653,0x2672,0x1611,0x0630,0x76D7,0x66F6,0x5695,0x46B4,
+0xB75B,0xA77A,0x9719,0x8738,0xF7DF,0xE7FE,0xD79D,0xC7BC,
+0x48C4,0x58E5,0x6886,0x78A7,0x0840,0x1861,0x2802,0x3823,
+0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969,0xA90A,0xB92B,
+0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,
+0xDBFD,0xCBDC,0xFBBF,0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A,
+0x6CA6,0x7C87,0x4CE4,0x5CC5,0x2C22,0x3C03,0x0C60,0x1C41,
+0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,0x8D68,0x9D49,
+0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13,0x2E32,0x1E51,0x0E70,
+0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78,
+0x9188,0x81A9,0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,
+0x1080,0x00A1,0x30C2,0x20E3,0x5004,0x4025,0x7046,0x6067,
+0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,0xE37F,0xF35E,
+0x02B1,0x1290,0x22F3,0x32D2,0x4235,0x5214,0x6277,0x7256,
+0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,
+0x34E2,0x24C3,0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405,
+0xA7DB,0xB7FA,0x8799,0x97B8,0xE75F,0xF77E,0xC71D,0xD73C,
+0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,0x4615,0x5634,
+0xD94C,0xC96D,0xF90E,0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,
+0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3,
+0xCB7D,0xDB5C,0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,
+0x4A75,0x5A54,0x6A37,0x7A16,0x0AF1,0x1AD0,0x2AB3,0x3A92,
+0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,0x9DE8,0x8DC9,
+0x7C26,0x6C07,0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1,
+0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9,0x9FF8,
+0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0};
+
+int checksum(char *packetstart,char *data,int size)
+{
+ char buf[64];
+ int sum,i,a,b;
+
+ if(size>0x3c) size=0x3c;
+ memcpy(buf,data,size);
+
+ b =(packetstart[0]&255);
+ b|=(packetstart[1]&255)<<8;
+ b|=(packetstart[2]&255)<<16;
+ b|=(packetstart[3]&255)<<24;
+ b&=0x7fffffff;
+ a=b%1020;
+
+ buf[size+0]=inittable[a+0]^(b);
+ buf[size+1]=inittable[a+1];
+ buf[size+2]=inittable[a+2]^(b>>8);
+ buf[size+3]=inittable[a+3];
+ size+=4;
+
+// printf("b=%02X ebp=%02X %02X %02X %02X\n",b,buf[size-4],buf[size-3],buf[size-2],buf[size-1]);
+
+ sum=0xffff;
+ for(i=0;i<size;i++)
+ {
+ a=(sum&0xffff) >> 8;
+ a^=buf[i]&255;
+ b=(sum&0xff) << 8;
+ sum=checktable[a]^b;
+ }
+
+ return(sum&255);
+}
+
+int floodprot(User *u)
+{
+ if(u->floodprot && u->time<u->time_lastsay[0]+4100)
+ {
+ q_print(u,"proxy: flood protect protect\n");
+ return(1);
+ }
+
+ u->time_lastsay[0]=u->time_lastsay[1];
+ u->time_lastsay[1]=u->time_lastsay[2];
+ u->time_lastsay[2]=u->time_lastsay[3];
+ u->time_lastsay[3]=u->time;
+
+ return(0);
+}
+
+void dmmchange(User *u,int newdmm)
+{
+ u->originaldmm=newdmm;
+ u->dmm=newdmm;
+ if(u->dmm<1) u->dmm=1;
+ if(u->dmm>3) u->dmm=3;
+ if(u->forcedmm) u->dmm=u->forcedmm;
+
+ if(u->dmm==1) u->dmmitemmask=~0;
+ else u->dmmitemmask=~(CAMP_RL|CAMP_LG|CAMP_GL|CAMP_SNG|CAMP_NG|CAMP_SSG);
+
+// q_printf(u,"dmm=%i, mask=%08X\n",u->dmm,u->dmmitemmask);
+}
+
+int text2color(char *p)
+{
+ int c;
+ char *p2;
+
+ if(p[0]=='t' || p[0]=='T') return(-2);
+ else if(p[0]=='r' || p[0]=='R') return(-3);
+ else if(*p=='*' || !*p || *p<'0' || *p>'9') return(-1);
+
+ p2=p;
+ while(*p2 && *p2>32)
+ {
+ if(*p2==',') break;
+ p2++;
+ }
+
+ if(*p2==',')
+ {
+ c=(atoi(p2+1)&15)+16*(atoi(p)&15)+256;
+ }
+ else
+ {
+ c=(atoi(p)&15)+16*(atoi(p)&15)+256;
+ }
+
+ return(c);
+}
+
+char *color2text(int c)
+{
+ static char buf[64];
+ if(c==-1) sprintf(buf,"* ");
+ else if(c==-2) sprintf(buf,"Team ");
+ else if(c==-3) sprintf(buf,"Rank ");
+ else if(c<255) sprintf(buf,"%i ",c);
+ else sprintf(buf,"%i,%i ",(c>>4)&15,c&15);
+ return(buf);
+}
+
--- /dev/null
+cc -ocheapo -DUNIXALPHA -lm -w1 -fast main.c proxy.c cmd.c misc.c compress.c
--- /dev/null
+#!/bin/sh
+# Legacy one-line build; prefer "make".
+gcc -O2 -o cheapo main.c proxy.c cmd.c misc.c compress.c -lm
--- /dev/null
+gcc -O2 -o cheapo main.c proxy.c cmd.c misc.c compress.c -lm -lsocket -lnsl
--- /dev/null
+nmake /f Makefile.win\r
--- /dev/null
+// Cheapo - a QuakeWorld Proxy
+// Copyright (C) 1998 Sami Tammilehto
+//
+// 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.
+
+#include "proxy.h"
+
+#define abs(x) ((x)<0?-(x):(x))
+
+uchar tmpbuf[BUFSIZE];
+uchar outbuf[BUFSIZE];
+
+User *globalu;
+int proxy_port;
+char quakepath[MAXSTR];
+char quakeoptions[MAXSTR];
+int gamespy;
+int quakemodified;
+int quakehandle; // process handle
+uchar proxy_name[MAXSTR];
+uchar proxy_allowip[MAXSTR];
+uchar proxy_denyip[16][MAXSTR];
+int proxy_denyipnum;
+uchar proxy_password[MAXSTR];
+int challenge=1634346416;
+int globaltime;
+int globaltime0;
+
+//#define RLPACKDEBUG
+
+#define DUP1 (*d++=*s++)
+#define DUPN(n) { for(duptmp=0;duptmp<(n);duptmp++) *d++=*s++; }
+#define DUPTEXT { while(*s) *d++=*s++; *d++=*s++; }
+#define SKIPTEXT { while(*s) s++; s++; }
+
+//-------------------------------------------------------
+
+// framelist for player model (0..142)
+// 00 axrun1 20 axstand4 40 pain6 60 deatha11 80 deathc11 100 deathe7 100 deathe7 120 axatt2 140 axattd4
+// 01 axrun2 21 axstand5 41 axdeth1 61 deathb1 81 deathc12 101 deathe8 101 deathe8 121 axatt3 141 axattd5
+// 02 axrun3 22 axstand6 42 axdeth2 62 deathb2 82 deathc13 102 deathe9 102 deathe9 122 axatt4 142 axattd6
+// 03 axrun4 23 axstand7 43 axdeth3 63 deathb3 83 deathc14 103 nailatt1 103 nailatt1 123 axatt5 . .
+// 04 axrun5 24 axstand8 44 axdeth4 64 deathb4 84 deathc15 104 nailatt2 104 nailatt2 124 axatt6 . .
+// 05 axrun6 25 axstand9 45 axdeth5 65 deathb5 85 deathd1 105 light1 105 light1 125 axattb1 . .
+// 06 rockrun1 26 axstand10 46 axdeth6 66 deathb6 86 deathd2 106 light2 106 light2 126 axattb2 . .
+// 07 rockrun2 27 axstand11 47 axdeth7 67 deathb7 87 deathd3 107 rockatt1 107 rockatt1 127 axattb3 . .
+// 08 rockrun3 28 axstand12 48 axdeth8 68 deathb8 88 deathd4 108 rockatt2 108 rockatt2 128 axattb4 . .
+// 09 rockrun4 29 axpain1 49 axdeth9 69 deathb9 89 deathd5 109 rockatt3 109 rockatt3 129 axattb5 . .
+// 10 rockrun5 30 axpain2 50 deatha1 70 deathc1 90 deathd6 110 rockatt4 110 rockatt4 130 axattb6 . .
+// 11 rockrun6 31 axpain3 51 deatha2 71 deathc2 91 deathd7 111 rockatt5 111 rockatt5 131 axattc1 . .
+// 12 stand1 32 axpain4 52 deatha3 72 deathc3 92 deathd8 112 rockatt6 112 rockatt6 132 axattc2 . .
+// 13 stand2 33 axpain5 53 deatha4 73 deathc4 93 deathd9 113 shotatt1 113 shotatt1 133 axattc3 . .
+// 14 stand3 34 axpain6 54 deatha5 74 deathc5 94 deathe1 114 shotatt2 114 shotatt2 134 axattc4 . .
+// 15 stand4 35 pain1 55 deatha6 75 deathc6 95 deathe2 115 shotatt3 115 shotatt3 135 axattc5 . .
+// 16 stand5 36 pain2 56 deatha7 76 deathc7 96 deathe3 116 shotatt4 116 shotatt4 136 axattc6 . .
+// 17 axstand1 37 pain3 57 deatha8 77 deathc8 97 deathe4 117 shotatt5 117 shotatt5 137 axattd1 . .
+// 18 axstand2 38 pain4 58 deatha9 78 deathc9 98 deathe5 118 shotatt6 118 shotatt6 138 axattd2 . .
+// 19 axstand3 39 pain5 59 deatha10 79 deathc10 99 deathe6 119 axatt1 119 axatt1 139 axattd3 . .
+
+#define LASTPLAYERFRAME 142
+char *playeranim[150]={
+"axrun1","axrun2","axrun3","axrun4","axrun5","axrun6","rockrun1","rockrun2",
+"rockrun3","rockrun4","rockrun5","rockrun6","stand1","stand2","stand3",
+"stand4","stand5","axstand1","axstand2","axstand3","axstand4","axstand5",
+"axstand6","axstand7","axstand8","axstand9","axstand10","axstand11",
+"axstand12","axpain1","axpain2","axpain3","axpain4","axpain5","axpain6",
+"pain1","pain2","pain3","pain4","pain5","pain6","axdeth1","axdeth2","axdeth3",
+"axdeth4","axdeth5","axdeth6","axdeth7","axdeth8","axdeth9","deatha1",
+"deatha2","deatha3","deatha4","deatha5","deatha6","deatha7","deatha8",
+"deatha9","deatha10","deatha11","deathb1","deathb2","deathb3","deathb4",
+"deathb5","deathb6","deathb7","deathb8","deathb9","deathc1","deathc2",
+"deathc3","deathc4","deathc5","deathc6","deathc7","deathc8","deathc9",
+"deathc10","deathc11","deathc12","deathc13","deathc14","deathc15","deathd1",
+"deathd2","deathd3","deathd4","deathd5","deathd6","deathd7","deathd8",
+"deathd9","deathe1","deathe2","deathe3","deathe4","deathe5","deathe6",
+"deathe7","deathe8","deathe9","nailatt1","nailatt2","light1","light2",
+"rockatt1","rockatt2","rockatt3","rockatt4","rockatt5","rockatt6","shotatt1",
+"shotatt2","shotatt3","shotatt4","shotatt5","shotatt6","axatt1","axatt2",
+"axatt3","axatt4","axatt5","axatt6","axattb1","axattb2","axattb3",
+"axattb4","axattb5","axattb6","axattc1","axattc2","axattc3","axattc4",
+"axattc5","axattc6","axattd1","axattd2","axattd3","axattd4","axattd5",
+"axattd6"};
+
+char *colornames[16]={
+"white","brown","lt.blue","green",
+"red","olive","pink","flesh",
+"lavender","purple","beige","aqua",
+"yellow","blue","blue","blue"};
+
+//-------------------------------------------------------hilo
+
+int intat(uchar *a)
+{
+ int b;
+ b =((uchar *)a)[0];
+ b|=((uchar *)a)[1]<<8;
+ b|=((uchar *)a)[2]<<16;
+ b|=((uchar *)a)[3]<<24;
+ return(b);
+}
+
+int shortat(uchar *a)
+{
+ int b;
+ b =((uchar *)a)[0];
+ b|=((uchar *)a)[1]<<8;
+ return(b);
+}
+
+void setintat(uchar *a,int b)
+{
+ ((uchar *)a)[0]=(b>>0 )&255;
+ ((uchar *)a)[1]=(b>>8 )&255;
+ ((uchar *)a)[2]=(b>>16)&255;
+ ((uchar *)a)[3]=(b>>24)&255;
+}
+
+#ifdef HILO
+
+int _dup2(uchar **d,uchar **s)
+{
+ int x;
+ (*d)[0]=(*s)[0];
+ (*d)[1]=(*s)[1];
+ x=(*s)[0];
+ x|=(*s)[1]<<8;
+ *s+=2;
+ *d+=2;
+ return((int)(short)x);
+}
+
+int _dup4(uchar **d,uchar **s)
+{
+ int x;
+ (*d)[0]=(*s)[0];
+ (*d)[1]=(*s)[1];
+ (*d)[2]=(*s)[2];
+ (*d)[3]=(*s)[3];
+ x=(*s)[0];
+ x|=(*s)[1]<<8;
+ x|=(*s)[2]<<16;
+ x|=(*s)[3]<<24;
+ *s+=4;
+ *d+=4;
+ return(x);
+}
+
+#define DUP2 _dup2(&d,&s)
+#define DUP4 _dup4(&d,&s)
+
+#else
+
+#define DUP2 (*(((short *)d)++)=*(((short *)s)++))
+#define DUP4 (*(((unsigned int *)d)++)=*(((unsigned int *)s)++))
+
+#endif
+
+//-------------------------------------------------------
+
+void checkpowers(User *u)
+{
+ int powers,changed,value;
+
+ powers=ITEMS_Q|ITEMS_P|ITEMS_R;
+ if(u->fortress) powers|=ITEMS_FLAG;
+
+ changed=u->power^u->lastpower;
+ value=u->power&powers;
+
+ if(u->amspectator || !(changed&powers)) return;
+ u->lastpower=u->power;
+
+ if((value&changed)==0)
+ {
+ // change to "got" power state
+ u->lastnonzeropower=changed;
+ }
+
+ if(u->autoihave&2)
+ {
+ if(changed&ITEMS_Q)
+ {
+ if(value&ITEMS_Q) q_stufftext(u,"f_powerup_quad\n");
+ else q_stufftext(u,"f_powerup_noquad\n");
+ }
+ if(changed&ITEMS_R)
+ {
+ if(value&ITEMS_R) q_stufftext(u,"f_powerup_ring\n");
+ else q_stufftext(u,"f_powerup_noring\n");
+ }
+ if(changed&ITEMS_P)
+ {
+ if(value&ITEMS_P) q_stufftext(u,"f_powerup_pent\n");
+ else q_stufftext(u,"f_powerup_nopent\n");
+ }
+ if(changed&ITEMS_FLAG)
+ {
+ if(value&ITEMS_FLAG) q_stufftext(u,"f_powerup_flag\n");
+ else q_stufftext(u,"f_powerup_noflag\n");
+ }
+ }
+
+ if(u->autoihave&1)
+ {
+ if(value&powers) q_stufftext(u,"f_powerup\n");
+ }
+}
+
+void cl_updatestat(User *u,int stat,int value)
+{
+ if(stat==0)
+ {
+ if(u->health>0 && value<=0)
+ {
+ int i;
+ findnear(u,u->pos[0],u->pos[1],u->pos[2],&u->diedatloc);
+ u->time_died=u->time;
+
+ u->playerloc[u->entity]=0;
+ u->playerlastloc[u->entity]=0;
+
+ u->diedatown=-1;
+ u->diedatenemy=0;
+ for(i=0;i<u->numplayers;i++)
+ {
+ if(u->playernear[i] && u->playeractive[i])
+ {
+ int dist;
+ dist =abs(u->playerpos[i][0]-u->pos[0]);
+ dist+=abs(u->playerpos[i][1]-u->pos[1]);
+ dist+=abs(u->playerpos[i][2]-u->pos[2]);
+ if(dist<NEARDIST)
+ {
+ if(SAMETEAM(i))
+ {
+ u->diedatown++;
+ }
+ else
+ {
+ u->diedatenemy++;
+ }
+ }
+ }
+ }
+ }
+ u->health=value;
+ }
+ if(stat==3) u->ammo=value;
+ if(stat==4) u->armor=value;
+ if(stat==6) u->ammo_shells=value;
+ if(stat==7) u->ammo_nails=value;
+ if(stat==8) u->ammo_rockets=value;
+ if(stat==9) u->ammo_cells=value;
+ if(stat==10) u->weapon=value;
+ if(stat==15)
+ {
+ u->items=value;
+ u->power=u->items;
+ checkpowers(u);
+ }
+}
+
+void campsound(User *u,uchar *sound,int entity,int x,int y,int z)
+{
+ Camp *ca;
+ int i;
+/* uchar *pname="action"; */
+
+ if(u->amspectator) return;
+
+/*
+ int locindex;
+ uchar *pos;
+ if(entity>0 && entity<=32 && u->playeractive[entity])
+ {
+ static int cnt=0;
+ pname=u->playername[entity];
+ if(!memcmp(sound,"weapons/sgun1",13))
+ {
+#ifdef RLPACKDEBUG
+ q_printf(u,"<%i: %s launched>\n",cnt++,pname);
+#endif
+ u->time_playerrocket[entity]=u->time;
+ }
+ if(!memcmp(sound,"player/",7))
+ {
+ if(!memcmp(sound+7,"death",5) ||
+ !memcmp(sound+7,"gib",3) ||
+ !memcmp(sound+7,"teledth",7) ||
+ !memcmp(sound+7,"udeath",6) ||
+ !memcmp(sound+7,"h2odeat",7))
+ {
+#ifdef RLPACKDEBUG
+ q_printf(u,"<%i: %s died>\n",cnt++,pname);
+#endif
+ u->time_playerdied[entity]=u->time;
+ }
+ }
+ }
+
+ if(loglevel>LOG_DEBUG)
+ {
+ pos=findnear(u,x,y,z,&locindex);
+ logx(LOG_DEBUG,"Sound: %s near %s sound %s (e:%03X pos:%i,%i,%i)\n"
+ ,pname,pos,sound,entity,x,y,z);
+ }
+*/
+
+ for(i=0;i<u->camps;i++)
+ {
+ ca=&u->camp[i];
+ if(abs(x-ca->pos[0])<TOOKRADIUS &&
+ abs(y-ca->pos[1])<TOOKRADIUS &&
+ abs(z-ca->pos[2])<TOOKRADIUS &&
+ strstr(sound,ca->pickupsound))
+ {
+#ifdef TIMERS
+ if(u->campthese && !(u->fpdcopy&FPD_TIMERS))
+ {
+ if(ca->category&CAMP_MH)
+ {
+ if(u->entity==entity)
+ {
+ u->time_nextcamp=u->time;
+ ca->appeartime=UNKNOWNTIME2; // other players will get this
+ ca->appeartime=UNKNOWNTIME;
+ ca->secondcamp=0;
+ }
+ }
+ else
+ {
+ ca->appeartime=u->time+ca->delay;
+ ca->secondcamp=0;
+ u->time_nextcamp=u->time;
+ }
+ }
+#endif
+
+#if 0
+ if(u->autoqpicked && !(u->fpd&FPD_TIMERS) && (ca->category&CAMP_POWER))
+ { // took an item
+ uchar buf[64];
+ *buf=0;
+ if(ca->category&CAMP_Q)
+ {
+ sprintf(buf,"say_team Quad #%c picked up.%cq",ca->name[1],0x7f);
+ }
+ if(ca->category&CAMP_P)
+ {
+ sprintf(buf,"say_team Pent #%c picked up.%cp",ca->name[1],0x7f);
+ }
+ if(ca->category&CAMP_R)
+ {
+ sprintf(buf,"say_team Ring #%c picked up.%cr",ca->name[1],0x7f);
+ }
+ if(!floodprot(u))
+ {
+ q_servercmd(u,buf);
+ }
+ }
+#endif
+
+ if(entity==u->entity)
+ {
+ sprintf(u->lasttook,"%s at %s",
+ u->entityname[ca->entity],
+ findnear(u,
+ u->entitypos[ca->entity][0],
+ u->entitypos[ca->entity][1],
+ u->entitypos[ca->entity][2],
+ NULL));
+ u->time_lasttook=u->time;
+ u->lastentitypicked=ca->entity;
+ if(!u->amspectator && u->autoitook)
+ {
+ if((ca->category&u->autoitookitems&u->dmmitemmask) ||
+ ((u->autoitookitems&CAMP_POINTED) && ca->entity==u->lastentitypointed) )
+ { // took an item
+ u->lastentitypointed=0;
+ q_stufftext(u,"f_took\n");
+ }
+ }
+ }
+
+ return;
+ }
+ }
+
+ // if not in camp list
+
+ if(entity==u->entity)
+ {
+ int lock=0;
+ if(strstr(sound,"lock")) lock=1;
+ if(strstr(sound,"pkup")) lock=1;
+ if(lock) for(i=0;i<MAXENTITIES;i++)
+ {
+ if(u->entitymask[i]&(CAMP_PACK|CAMP_ROCKETS|CAMP_CELLS|CAMP_RUNE))
+ {
+ if(abs(x-u->entitypos[i][0])<TOOKRADIUS &&
+ abs(y-u->entitypos[i][1])<TOOKRADIUS &&
+ abs(z-u->entitypos[i][2])<TOOKRADIUS)
+ {
+ sprintf(u->lasttook,"%s at %s",
+ u->entityname[i],
+ findnear(u,
+ u->entitypos[i][0],
+ u->entitypos[i][1],
+ u->entitypos[i][2],
+ NULL));
+ u->time_lasttook=u->time;
+ u->lastentitypicked=i;
+ if(!u->amspectator && u->autoitook)
+ {
+ if((u->entitymask[i]&u->autoitookitems&u->dmmitemmask) ||
+ ((u->autoitookitems&CAMP_POINTED) && i==u->lastentitypointed) )
+ { // took an item
+ u->lastentitypointed=0;
+ q_stufftext(u,"f_took\n");
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
+
+void fixuphidemsg(User *u,uchar *s0)
+{
+ static uchar buf[MAXSTR];
+ uchar *s,*d,*d2;
+ int mode=0,quote=0,sometext=0;
+
+ s=s0;
+ d=s0;
+
+ sprintf(buf,"(%s): ",u->name);
+ d2=buf+strlen(buf);
+
+ while(*s)
+ {
+ if(*s==0xff)
+ {
+ mode^=1;
+ if(mode==1)
+ {
+ *d2++='x';
+ *d2++='x';
+ *d2++='x';
+ }
+ }
+ else if(*s==0x7f) break;
+ else
+ {
+ if(s>s0+8 && *s!='\"' && mode==0) *d2++=*s;
+ if(*s!='\"') sometext=1;
+ *d++=*s;
+ }
+ s++;
+ }
+
+ *d2++='\n';
+ *d2++=0;
+
+ if(d[-1]=='\"')
+ {
+ d--;
+ quote=1;
+ }
+ if(sometext)
+ {
+ *d++=0x7f;
+ *d++='!';
+ *d++=u->entity+'@';
+ }
+ if(quote) *d++='\"';
+ *d++=0;
+
+ strand80(buf);
+ q_print(u,buf);
+}
+
+void appendlocname(User *u,uchar **d,uchar *p)
+{
+ char *p2;
+ while(*p)
+ {
+ if(*p=='%')
+ {
+ p++;
+ if(*p=='r' || *p=='R')
+ {
+ p++;
+ if(u->bcolor==4) p2="own";
+ else p2="enemy";
+ while(*p2) *(*d)++=*p2++;
+ }
+ else if(*p=='b' || *p=='B')
+ {
+ p++;
+ if(u->bcolor==4) p2="enemy";
+ else p2="own";
+ while(*p2) *(*d)++=*p2++;
+ }
+ else
+ {
+ *(*d)++='%';
+ *(*d)++=*p++;
+ }
+ }
+ *(*d)++=*p++;
+ }
+}
+
+int handlesaying(User *u,Userinfo *ui,uchar **d0,uchar **s0)
+{
+ uchar *d,*s,*p;
+ int modified=0;
+ int cmd,flag,ammo,bracket,pow;
+ uchar buf[MAXSTR];
+ d=*d0;
+ s=*s0;
+
+ // copy the say / say_team part
+ while(*s>32)
+ {
+ *d++=*s++;
+ }
+ if(*s==32) *d++=*s++;
+ *d0=d;
+ *s0=s;
+
+ // process rest
+ while(*s)
+ {
+ if(*s=='%' && !(u->fpd&FPD_PERCENT))
+ {
+ s++;
+ cmd=*s++;
+ if(cmd=='L' || cmd=='O' || cmd=='E')
+ {
+ if(u->time<u->time_died+5000 && u->diedatloc>=0 && u->diedatloc<u->locations)
+ {
+ // keep the command
+ }
+ else
+ {
+ cmd|=0x20; // use lowercase version
+ }
+ }
+ if(cmd==0)
+ {
+ *d++='%';
+ s--;
+ break;
+ }
+ else if(cmd=='[')
+ {
+ cmd=*s++;
+ if(*s==']') s++;
+ bracket=1;
+ }
+ else bracket=0;
+ switch(cmd)
+ {
+ case 'h':
+ if(ui->own) u->time_lastfreport=u->time;
+ if(bracket)
+ {
+ if(ui->health<50)
+ {
+ *d++=0x90;
+ }
+ else bracket=0;
+ sprintf(buf,"h:%i",ui->health);
+ p=buf; while(*p) *d++=*p++;
+ if(bracket)
+ {
+ *d++=0x91;
+ }
+ }
+ else
+ {
+ sprintf(buf,"%i",ui->health);
+ p=buf; while(*p) *d++=*p++;
+ }
+ break;
+ case 'a':
+ if(ui->own) u->time_lastfreport=u->time;
+ if(bracket)
+ {
+ if(ui->armor<50)
+ {
+ *d++=0x90;
+ }
+ else bracket=0;
+
+ if(ui->items&0x2000)
+ {
+ *d++='g';
+ }
+ if(ui->items&0x4000)
+ {
+ *d++='y';
+ }
+ if(ui->items&0x8000)
+ {
+ *d++='r';
+ }
+
+ sprintf(buf,"a:%i",ui->armor);
+
+ p=buf; while(*p) *d++=*p++;
+
+ if(bracket)
+ {
+ *d++=0x91;
+ }
+ }
+ else
+ {
+ sprintf(buf,"%i",ui->armor);
+ p=buf; while(*p) *d++=*p++;
+ }
+ break;
+ case 'A':
+ if(ui->items&0x2000)
+ {
+ *d++='g';
+ }
+ if(ui->items&0x4000)
+ {
+ *d++='y';
+ }
+ if(ui->items&0x8000)
+ {
+ *d++='r';
+ }
+ break;
+ case 'P': // %p
+ case 'p': // %p
+ if(cmd=='p') pow=ui->power;
+ else pow=u->lastnonzeropower;
+ flag=0;
+ if(bracket && (pow&(ITEMS_Q|ITEMS_P|ITEMS_R|ITEMS_FLAG)))
+ {
+ *d++=0x90;
+ }
+ else bracket=0;
+ if(pow&ITEMS_Q)
+ {
+ if(flag) p=" quad";
+ else p="quad";
+ flag=1;
+ while(*p) *d++=*p++;
+ }
+ if(pow&ITEMS_P)
+ {
+ if(flag) p=" pent";
+ else p="pent";
+ flag=1;
+ while(*p) *d++=*p++;
+ }
+ if(pow&ITEMS_R)
+ {
+ if(flag) p=" ring";
+ else p="ring";
+ flag=1;
+ while(*p) *d++=*p++;
+ }
+ if(pow&ITEMS_FLAG)
+ {
+ if(flag) p=" flag";
+ else p="flag";
+ flag=1;
+ while(*p) *d++=*p++;
+ }
+ if(bracket)
+ {
+ *d++=0x91;
+ }
+ break;
+ case 's': // %s (what is low)
+ {
+ int flag=0;
+ if(ui->armor<50)
+ {
+ if(ui->items&0x2000)
+ {
+ *d++='g';
+ }
+ if(ui->items&0x4000)
+ {
+ *d++='y';
+ }
+ if(ui->items&0x8000)
+ {
+ *d++='r';
+ }
+
+ sprintf(buf,"a:%i",ui->armor);
+
+ p=buf; while(*p) *d++=*p++;
+ flag=1;
+ }
+ if(ui->health<50)
+ {
+ if(flag) *d++=' ';
+ sprintf(buf,"h:%i",ui->health);
+ p=buf; while(*p) *d++=*p++;
+ flag=1;
+ }
+ if((ui->bestweapon==8-1) && ui->bestammo>=10)
+ { // LG with 10+ cells
+ break;
+ }
+ else if((ui->bestweapon==7-1) && ui->bestammo>=3)
+ { // RL with 3+ rockets
+ break;
+ }
+ if(flag) *d++=' ';
+ flag=1;
+ }
+ // doflow over to %b
+ case 'b': // %b (best weapon, rl first)
+ switch(ui->bestweapon)
+ {
+ default:
+ case 1-1: p="axe"; break;
+ case 2-1: p="sg"; break;
+ case 3-1: p="ssg"; break;
+ case 4-1: p="ng"; break;
+ case 5-1: p="sng"; break;
+ case 6-1: p="gl"; break;
+ case 7-1: p="rl"; break;
+ case 8-1: p="lg"; break;
+ }
+ ammo=ui->bestammo;
+
+ if((ui->bestweapon==8-1) && ui->bestammo>=10)
+ { // LG with 10+ cells
+ bracket=0;
+ }
+ else if((ui->bestweapon==7-1) && ui->bestammo>=3)
+ { // RL with 3+ rockets
+ bracket=0;
+ }
+
+ if(bracket)
+ {
+ *d++=0x90;
+ }
+ while(*p) *d++=*p++;
+ if(*p!='a')
+ {
+ *d++=':';
+ sprintf(buf,"%i",ammo);
+ p=buf; while(*p) *d++=*p++;
+ }
+ if(bracket)
+ {
+ *d++=0x91;
+ }
+ break;
+ case 'w': // %w
+ switch(ui->weapon)
+ {
+ default:
+ case 1-1: p="axe"; break;
+ case 2-1: p="sg"; break;
+ case 3-1: p="ssg"; break;
+ case 4-1: p="ng"; break;
+ case 5-1: p="sng"; break;
+ case 6-1: p="gl"; break;
+ case 7-1: p="rl"; break;
+ case 8-1: p="lg"; break;
+ }
+ ammo=ui->ammo;
+
+ if((ui->weapon==8-1) && ui->ammo>=10)
+ { // LG with 10+ cells
+ bracket=0;
+ }
+ else if((ui->weapon==7-1) && ui->ammo>=3)
+ { // RL with 3+ rockets
+ bracket=0;
+ }
+
+ if(bracket)
+ {
+ *d++=0x90;
+ }
+ while(*p) *d++=*p++;
+ if(*p!='a')
+ {
+ *d++=':';
+ sprintf(buf,"%i",ammo);
+ p=buf; while(*p) *d++=*p++;
+ }
+ if(bracket)
+ {
+ *d++=0x91;
+ }
+ break;
+ case 'q': // %q
+ {
+ int flag=0;
+ if(u->time<u->time_lastenemymask+10000)
+ {
+ if(u->lastenemymask&CAMP_Q)
+ {
+ if(flag) p=" quad";
+ else p="quad";
+ while(*p) *d++=*p++;
+ flag=1;
+ }
+ if(u->lastenemymask&CAMP_P)
+ {
+ if(flag) p=" pent";
+ else p="pent";
+ while(*p) *d++=*p++;
+ flag=1;
+ }
+ if(u->lastenemymask&CAMP_R)
+ {
+ if(flag) p=" ring";
+ else p="ring";
+ while(*p) *d++=*p++;
+ flag=1;
+ }
+ }
+ if(flag==0)
+ {
+ p="quad";
+ while(*p) *d++=*p++;
+ }
+ }
+ break;
+ case 'g': // %g
+ {
+#ifdef TIMERS
+ int flag=0;
+ if(!(u->fpd&FPD_TIMERS) && u->campthese)
+ {
+ int i;
+ Camp *ca;
+
+ for(i=0;i<u->camps;i++)
+ {
+ ca=u->camp+i;
+ p=NULL;
+ if((ca->category&u->campthese) && ca->appeartime>u->time-5000 && ca->appeartime<u->time+10000)
+ {
+ if(ca->category&(CAMP_Q|CAMP_P|CAMP_R))
+ {
+ p=ca->genericname;
+ }
+ }
+ if(p)
+ {
+ if(flag) *d++=' ';
+ while(*p) *d++=*p++;
+ flag=1;
+ }
+ }
+ }
+ if(flag==0)
+ {
+ p="quad";
+ while(*p) *d++=*p++;
+ }
+#endif
+ {
+ p="quad";
+ while(*p) *d++=*p++;
+ }
+ }
+ break;
+ case 'r': // %r last reported loc
+ if(u->lastreportedloc>=0 && u->lastreportedloc<u->locations)
+ {
+ p=u->loc[u->lastreportedloc].name;
+ appendlocname(u,&d,p);
+ }
+ break;
+ case 'd': // (loc died at)
+ case 'L': // (loc died at (5 sec limit))
+ if(u->diedatloc>=0 && u->diedatloc<u->locations)
+ {
+ p=u->loc[u->diedatloc].name;
+ u->lastreportedloc=u->diedatloc;
+ appendlocname(u,&d,p);
+ }
+ break;
+ case 'U':
+ u->trackloc=1;
+ p=playerlocdirection(u,u->entity);
+ if(p)
+ {
+ while(*p) *d++=*p++;
+ break;
+ }
+ // else use normal %l
+ case 'l': // %l
+ {
+ uchar *n;
+ int nind;
+ n=findnear(u,ui->x,ui->y,ui->z,&nind);
+ u->lastreportedloc=nind;
+ p=n;
+ appendlocname(u,&d,p);
+ }
+ break;
+ case 'x': // %x (item only)
+ case 'y': // %y (pos only)
+ case 't': // %t
+ case 'T': // %T
+ {
+ uchar *n;
+ int ent;
+ if(cmd=='x')
+ {
+ n=findpoint(u,u->pos[0],u->pos[1],u->pos[2],u->ang[0],u->ang[1],u->ang[2],u->pointtell&u->dmmitemmask,
+ POINT_ADDSKIN|POINT_ONLYNAME,&ent);
+ }
+ else if(cmd=='y')
+ {
+ n=findpoint(u,u->pos[0],u->pos[1],u->pos[2],u->ang[0],u->ang[1],u->ang[2],u->pointtell&u->dmmitemmask,
+ POINT_ADDSKIN|POINT_ONLYPOS,&ent);
+ }
+ else
+ {
+ int flag=POINT_ADDSKIN;
+ if(cmd=='T')
+ {
+ flag|=POINT_DIRECTION;
+ u->trackloc=1;
+ }
+ n=findpoint(u,u->pos[0],u->pos[1],u->pos[2],u->ang[0],u->ang[1],u->ang[2],u->pointtell&u->dmmitemmask,
+ flag,&ent);
+ }
+ if(*n!='.')
+ {
+ if(cmd!='y')
+ {
+ u->lastentitypointed=ent;
+ strncpy(u->lastpoint,n,63);
+ u->time_lastpoint=u->time;
+ }
+ }
+ else n="";
+ if(u->fpd&FPD_POINT)
+ {
+ *d++=0xff;
+ p=n; while(*p) *d++=*p++;
+ *d++=0xff;
+ modified|=2;
+ }
+ else
+ {
+ p=n; while(*p) *d++=*p++;
+ }
+ }
+ break;
+ case 'i':
+ if(u->time<u->time_lasttook+10000)
+ {
+ p=u->lasttook;
+ while(*p) *d++=*p++;
+ }
+ break;
+ case 'j':
+ if(u->time<u->time_lastpoint+10000)
+ {
+ if(u->fpd&FPD_POINT)
+ {
+ modified=2;
+ *d++=0xff;
+ }
+
+ p=u->lastpoint;
+ while(*p) *d++=*p++;
+
+ if(u->fpd&FPD_POINT)
+ {
+ modified=2;
+ *d++=0xff;
+ }
+ }
+ break;
+ case 'm':
+ if(u->fpd&FPD_ITEM)
+ {
+ p="(item at someplace)";
+ while(*p) *d++=*p++;
+ }
+ else
+ {
+ if(u->time_lasttook>u->time_lastpoint && u->time<u->time_lasttook+5000)
+ {
+ p=u->lasttook;
+ while(*p) *d++=*p++;
+ }
+ else if(u->time<u->time_lastpoint+5000)
+ {
+ if(u->fpd&FPD_POINT)
+ {
+ modified=2;
+ *d++=0xff;
+ }
+
+ p=u->lastpoint;
+ while(*p) *d++=*p++;
+
+ if(u->fpd&FPD_POINT)
+ {
+ modified=2;
+ *d++=0xff;
+ }
+ }
+ else
+ {
+ p=findnearestitem(u,u->pos[0],u->pos[1],u->pos[2],u->pointtell&u->dmmitemmask);
+ if(*p!='.') while(*p) *d++=*p++;
+ }
+ }
+ break;
+ case 'k':
+ if(u->time_lasttook>u->time_lastpoint && u->time<u->time_lasttook+10000)
+ {
+ p=u->lasttook;
+ while(*p) *d++=*p++;
+ }
+ else if(u->time<u->time_lastpoint+10000)
+ {
+ if(u->fpd&FPD_POINT)
+ {
+ modified=2;
+ *d++=0xff;
+ }
+
+ p=u->lastpoint;
+ while(*p) *d++=*p++;
+
+ if(u->fpd&FPD_POINT)
+ {
+ modified=2;
+ *d++=0xff;
+ }
+ }
+ break;
+ case 'O':
+ {
+ sprintf(buf,"%i",u->diedatown);
+ p=buf; while(*p) *d++=*p++;
+ }
+ break;
+ case 'E':
+ {
+ sprintf(buf,"%i",u->diedatenemy);
+ p=buf; while(*p) *d++=*p++;
+ }
+ break;
+
+ case 'e': // %e
+ if(u->fpd&FPD_PERCENTE)
+ {
+ *d++='x';
+ break;
+ }
+ case 'o': // %o
+ {
+ int cnt=0,i;
+ for(i=0;i<u->numplayers;i++)
+ {
+ if(u->playernear[i] && u->playeractive[i])
+ {
+ int dist;
+ dist =abs(u->playerpos[i][0]-u->pos[0]);
+ dist+=abs(u->playerpos[i][1]-u->pos[1]);
+ dist+=abs(u->playerpos[i][2]-u->pos[2]);
+ if(dist<NEARDIST)
+ {
+ if(SAMETEAM(i))
+ {
+ if(cmd=='o' && u->playeractive[i]) cnt++;
+ }
+ else
+ {
+ if(cmd=='e' && u->playeractive[i]) cnt++;
+ }
+ }
+ }
+ }
+ sprintf(buf,"%i",cnt);
+ p=buf; while(*p) *d++=*p++;
+ }
+ break;
+ case 'N':
+ *d++=0x7f;
+ *d++='!';
+ *d++=u->entity+'@';
+ break;
+ case 'n': // %n
+ u->time_lastfreport=u->time;
+ if(u->usepercentn)
+ {
+ int i;
+ *d++=0x7f;
+ for(i=0;i<u->numplayers;i++)
+ {
+ if(i==u->entity || u->playernear[i])
+ {
+ *d++=i+'@';
+ }
+ }
+ }
+ break;
+ case 'S': // skin
+ p=u->playerskin[u->entity];
+ while(*p && *p!='.') *d++=*p++;
+ break;
+ case 'C': // color
+ p=colornames[u->bcolor&15];
+ while(*p) *d++=*p++;
+ break;
+ case '%':
+ *d++='%';
+ break;
+ default:
+ *d++='%';
+ *d++=cmd;
+ break;
+ }
+ modified|=1;
+ }
+ else *d++=*s++;
+ }
+ *d++=*s++; // final zero
+
+ modified|=funnyconvert(*d0,*d0,1);
+
+ // color buf fix at start of line
+ //logx(LOG_DEBUG,"colorfix<%s>\n",*d0);
+ {
+ uchar *p=*d0;
+ while(*p && *p==0xff) p++;
+ if(*p&0x80)
+ {
+ //logx(LOG_DEBUG,"color[%i] %02X, fixed\n",p-*d0,*p);
+ memmove(*d0+1,*d0,d-*d0);
+ **d0='\"';
+ *d++='\"';
+ *d++=0;
+ }
+ }
+
+ *d0=*d0+strlen(*d0)+1;
+ *s0=s;
+ return(modified);
+}
+
+void handleserver(User *u,uchar *buffer,int bytes,uchar *outbuf,int *outbytes)
+{
+ unsigned int *p=(unsigned int *)buffer;
+ uchar buf[MAXSTR];
+ int duptmp;
+ int modified=0;
+ uchar *s,*s_end,*d;
+ int cmd,cmdpos = 0;
+ int dump=loglevel>=LOG_PACKETS;
+ static int packlist[32];
+ int packlistnum=0;
+ int lastcmd = 0;
+
+ u->choke=0;
+
+ if(u->safemode)
+ {
+ memcpy(outbuf,buffer,bytes);
+ *outbytes=bytes;
+ return;
+ }
+
+ if(u->fpd&FPD_TIMERS) u->campthese=0;
+
+ if(*p==0xffffffff)
+ {
+ if(buffer[4]=='c')
+ {
+ u->challenge=atoi(buffer+5);
+ q_command(u,"!challenge");
+ *outbytes=0; // remove
+ }
+ else if(buffer[4]=='j')
+ {
+ q_command(u,"!accept");
+ *outbytes=0; // remove
+ }
+ else
+ {
+ memcpy(outbuf,buffer,bytes);
+ *outbytes=bytes;
+ }
+ return;
+ }
+
+ s=buffer;
+ s_end=buffer+bytes;
+ d=outbuf;
+ // copy headers
+ DUPN(8);
+ // copy rest
+ cmd=255;
+ while(s<s_end)
+ {
+ if(dump) logx(LOG_PACKETS,"%02X@%02X: ",*s,s-buffer);
+ cmdpos=s-buffer;
+ lastcmd=cmd;
+ cmd=DUP1;
+ switch(cmd)
+ {
+ case 0x01:
+ {
+ if(dump) logx(LOG_PACKETS,"nop\n");
+ } break;
+ case 0x02:
+ {
+ if(dump) logx(LOG_PACKETS,"disconnect\n");
+ modified=1;
+ // skip command
+ d--;
+ // send changing map command
+ q_command(u,"!sdisconnect");
+ } break;
+ case 0x03:
+ {
+ int stat,value;
+ if(dump) logx(LOG_PACKETS,"updatestat\n");
+ stat=DUP1;
+ value=DUP1;
+ cl_updatestat(u,stat,value);
+ } break;
+ case 0x06:
+ {
+ int mix,vol,attenuation,channel,entity,soundnum;
+ int pos[3];
+ if(dump) logx(LOG_PACKETS,"sound\n");
+ mix=DUP2;
+ if(mix&0x8000) vol=DUP1; else vol=256;
+ if(mix&0x4000) attenuation=DUP1; else attenuation=64;
+ channel=mix&7;
+ entity=(mix>>3)&0x3ff;
+ soundnum=DUP1;
+ pos[0]=DUP2;
+ pos[1]=DUP2;
+ pos[2]=DUP2;
+ campsound(u,u->sound[soundnum],entity,pos[0],pos[1],pos[2]);
+ } break;
+ case 0x08:
+ {
+ int level,i,j,showthis = 0,negate;
+ int nojoin,team=0;
+ char filter[16];
+ uchar *d0=d-1,*d1;
+
+ *filter=0;
+
+ level=DUP1;
+ if(dump) logx(LOG_PACKETS,"print%i: %s\n",level,formatqstring(s));
+ d1=d;
+
+ if((*s&0x7f)=='(') team=1;
+
+ nojoin=0;
+ for(;;)
+ {
+ // duptext, check if 0x7f or ENTER in it
+ while(*s)
+ {
+ if(*s==0x7f || *s==0x0a) nojoin=1;
+ if(*s==13 && !team)
+ {
+ // remove fake enter if not team message
+ *d++=' ';
+ s++;
+ modified=1;
+ }
+ else
+ {
+ *d++=*s++;
+ }
+ }
+ *d++=*s++;
+
+ if(nojoin) break; // nojoin
+
+ // do we have a following text with same level?
+ if(s+3<s_end && s[0]==0x08 && s[1]==level)
+ {
+ d--; // remove end of last text
+ s+=2; // skip next command start
+ modified=1;
+ continue; // next text
+ }
+
+ break;
+ }
+
+ if(u->usepercentn && !u->linkedclient)
+ {
+ int sync=0;
+ showthis=1;
+ negate=0;
+ for(i=strlen(d1)-1;i>=0;i--)
+ {
+ if(d1[i]==0x7f)
+ { // %n filter
+ showthis=0;
+ for(j=i+1;d1[j]>32;j++)
+ {
+ if(d1[j]==u->entity+'@') showthis=1;
+ else if(d1[j]=='r') sync=CAMP_R;
+ else if(d1[j]=='q') sync=CAMP_Q;
+ else if(d1[j]=='p') sync=CAMP_P;
+ else if(d1[j]=='!') negate=1;
+ //d1[j]=' ';
+ }
+
+ d1[i]='\n';
+ d1[i+1]=0;
+ d=d1+i+2;
+
+ modified=1;
+ break;
+ }
+ else if(d1[i]=='#' && d1[i-1]==' ' && u->filternum)
+ {
+ for(j=0;j<8 && d1[i+j]>32;j++) filter[j]=d1[i+j];
+ filter[j]=0;
+ d1[i]='\n';
+ d1[i+1]=0;
+ d=d1+i+2;
+
+ modified=1;
+ break;
+ }
+ }
+ if(negate) showthis^=1;
+ }
+
+ if(*filter)
+ {
+ showthis=0;
+ for(i=0;i<u->filternum;i++)
+ {
+ if(!stricmp(filter,u->filter[i]))
+ {
+ showthis=1;
+ break;
+ }
+ }
+ }
+
+ // speech recognition
+ if(d!=d0)
+ {
+ static int c0='_'+10;
+ static int c1='f'+10;
+ static int c2='v'+10;
+ uchar *p=d1,*p0=d1;
+ int teammsg;
+
+ if( (p0[0]&0x7f)=='(' ) teammsg=1;
+ else teammsg=0;
+
+ while(*p) if((*p++)==':') break;
+ if(*p) p++; // skip the space
+ else break;
+
+ if(u->secondcamp)
+ {
+ int mask;
+ if(strstr(p,"i have") || strstr(p,"I have"))
+ {
+ mask=0;
+ if(strstr(p,"pent") && u->campcnt[4]==2) mask|=CAMP_P;
+ if(strstr(p,"quad") && u->campcnt[5]==2) mask|=CAMP_Q;
+ if(strstr(p,"ring") && u->campcnt[3]==2) mask|=CAMP_R;
+ }
+ }
+
+ if(showthis && u->soundtrigger && teammsg && !(u->fpd&FPD_SOUND))
+ {
+ int i=strlen(p)-1;
+ for(;i>0;i--) if(p[i]==u->soundtrigger) break;
+ if(p[i]==u->soundtrigger && p[i+1]>'@')
+ {
+ uchar buf[128];
+ strcpy(buf,"play ");
+ strncpy(buf+5,p+i+1,100);
+ strcat(buf,"\n");
+ q_stufftext(u,buf);
+ showthis=0;
+ if(i>0)
+ { // send starting text as a print
+ int a;
+ if(p[i-1]==32) p--;
+ a=p[i];
+ p[i]=0;
+ q_printf(u,"%s\n",p0);
+ p[i]=a;
+ }
+ }
+ }
+
+ if(u->autofreport && u->time>u->time_lastfreport+2000 && teammsg)
+ {
+ int cnt1=0,cnt2=0;
+ char *p2=p;
+ while(*p2)
+ {
+ if(*p2=='/') cnt1++;
+ if(*p2==':') cnt2++;
+ p2++;
+ }
+ if(cnt2>=3 || (cnt2>=1 && cnt1>=2))
+ {
+ int e,l;
+ // check the player is near
+ for(e=1;e<=32;e++)
+ {
+ if(u->playeractive[e])
+ {
+ l=strlen(u->playername[e]);
+ if(!memcmp7f(u->playername[e],p0+1,l))
+ { // sender found
+ if(e!=u->entity && u->playernear[e])
+ {
+ u->time_sendfreport=u->time+100+((u->bytestoserver>>4)&255);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(p[0]=='f' && p[1]=='_' && !u->nofversion)
+ {
+ uchar buf[64];
+ extern uchar *versionname,*version;
+ if(u->time>u->time_lastfversion+9000 && !floodprot(u))
+ {
+ u->time_lastfversion=u->time;
+ if(is_version(p+1))
+ { // f_version
+ char *d=buf,*s,*p1;
+ *d++='s';
+ *d++='a';
+ *d++='y';
+ *d++=' ';
+
+ s=versionname;
+ while(*s) *d++=*s++;
+
+ *d++=' ';
+
+ s=version;
+ while(*s) *d++=*s++;
+
+ *d++=' ';
+
+ // check salt
+ p1=p+1;
+ while(*p1 && *p1> 32) p1++;
+ while(*p1 && *p1<=32) p1++;
+ if(*p1)
+ {
+ //for(i=strlen(u->name);i<10;i++) *d++=' ';
+ codecrc(p1,d);
+ }
+ else
+ {
+#ifdef TIMERS
+ *d++='t';
+ *d++='i';
+ *d++='m';
+ *d++='e';
+ *d++='r';
+ *d++='s';
+#endif
+ *d++=' ';
+ statecrc(u,d);
+ }
+ q_servercmd(u,buf);
+ }
+ else if(u->campthese && is_timer(p+1))
+ { // f_timer
+ /*extern uchar *versionname,*version;*/
+ char *p=buf;
+ *p++='s';
+ *p++='a';
+ *p++='y';
+ *p++=' ';
+ *p++='<';
+ *p++='-';
+ *p++='l';
+ *p++='l';
+ *p++='a';
+ *p++='m';
+ *p++='a';
+ *p++=0;
+ q_servercmd(u,buf);
+ u->time_lastfversion=u->time;
+ }
+ else if(u->fmodified && is_modified(p+1))
+ { // f_modified
+ /*
+ c_textaddress(tmpbuf,&u->toclientaddr);
+ printf("gamedir=<%s> quakepath=<%s> from=<%s>\n",
+ u->gamedir,quakepath,tmpbuf);
+ */
+ strcpy(tmpbuf,"say ");
+#ifdef PROTECT
+#ifdef WIN32
+ if(!*quakepath)
+ {
+ strcat(tmpbuf,"qwcl started separately");
+ }
+ else if(!c_islocalconnect(&u->toclientaddr))
+ {
+ strcat(tmpbuf,"client not local");
+ }
+ else
+ {
+ if(quakemodified) strcat(tmpbuf,"executable modified, ");
+ strcat(tmpbuf,checkmodels(u->gamedir,p+10));
+ }
+#else
+ strcat(tmpbuf,"non-windows version");
+#endif
+#else
+ strcat(tmpbuf,"cheapo open");
+#endif
+ q_servercmd(u,tmpbuf);
+ q_stufftext(u,"flush\n");
+ }
+ }
+ }
+ else
+ {
+ if(p[0]==p[3] && (p[1]&0x7f)+10==c1 && (p[2]&0x7f)+10==c2 && (p[3]&0x7f)+10==c0)
+ {
+ uchar buf[16];
+ createversiontext(u,buf);
+ q_servercmd(u,buf);
+ }
+ }
+ }
+ if(!showthis)
+ { // not for me
+ d=d0;
+ modified=1;
+ }
+ } break;
+ case 0x09:
+ {
+ if(dump) logx(LOG_PACKETS,"stufftext: %s\n",formatqstring(s));
+ if(!memcmp(s,"proxyautoexec",13))
+ {
+ // we have linked!
+ q_print(u,"Linked to a new proxy.\n"
+ "First proxy responds to .cmd\n"
+ "Last proxy responds to proxy:cmd\n");
+ strcpy(u->proxysay,"say .");
+ q_servercmd(u,"cheapolink");
+ u->linkedserver=1;
+ // remove command
+ d--;
+ SKIPTEXT;
+ modified=1;
+ break;
+ }
+ else if(!memcmp(s,"fullserverinfo",14))
+ {
+ uchar mapname[MAXSTR];
+ uchar fpdtxt[8];
+ uchar *p;
+
+ fpdtxt[0]='f'+13;
+ fpdtxt[1]='p'+14;
+ fpdtxt[2]='d'+15;
+ fpdtxt[3]='\\'+16;
+ fpdtxt[4]=0;
+
+ extractfield(mapname,s,"deathmatch\\");
+ dmmchange(u,atoi(mapname));
+
+ u->fortress=0;
+ u->ctf=0;
+ extractfield(mapname,s,"*gamedir\\");
+ strcpy(u->gamedir,mapname);
+ if(strstr(mapname,"fortress"))
+ {
+ //q_print(u,"proxy: team fortress restrictions\n");
+ u->fortress=1;
+ u->skinoverride=0;
+ u->coloroverride=0;
+ }
+ else if(strstr(mapname,"ctf"))
+ {
+ u->ctf=1;
+ }
+
+ fpdtxt[5]=0;
+ fpdtxt[3]-=16;
+ fpdtxt[2]-=15;
+ fpdtxt[1]-=14;
+ fpdtxt[0]-=13;
+
+ extractfield(mapname,s,fpdtxt);
+ u->fpd=atoi(mapname)|litemode;
+ u->fpdcopy=atoi(mapname)|litemode;
+#ifdef TIMERS
+ if(u->notimers) u->fpd|=FPD_TIMERS;
+ if(u->notimers) u->fpdcopy|=FPD_TIMERS;
+#endif
+
+ extractfield(mapname,s,"map\\");
+
+ // setup crc for location
+ u->wherecrc=0;
+ p=mapname;
+ while(*p) { u->wherecrc+=*p++; u->wherecrc<<=4; }
+ u->wherecrc+=u->servermainaddr.sin_port;
+ u->wherecrc+=u->servermainaddr.sin_addr.s_addr;
+
+ if(!u->linkedclient)
+ {
+ q_loadloc(u,mapname);
+ logx(LOG_VERBOSE,"Map %s, %i locations.\n",mapname,u->locations);
+ reportfpd(u,u->fpd);
+ }
+
+ DUPTEXT;
+ break;
+ }
+ else
+ {
+ char *p,*d0=d;
+ int modhere=0,i;
+ if(*s=='c' && !memcmp(s,"changing",8))
+ {
+ u->changing=1;
+ }
+ while(*s)
+ {
+ if(*s=='c' && !memcmp(s,"connect",7))
+ {
+ s+=7;
+ p="say proxy:con";
+ while(*p) *d++=*p++;
+ modhere=1;
+ }
+ else if(*s=='d' && !memcmp(s,"disconnect",10))
+ {
+ s+=10;
+ p="say proxy:dis";
+ while(*p) *d++=*p++;
+ modhere=1;
+ }
+ else if(*s=='r' && !memcmp(s,"reconnect",9))
+ {
+ if(u->changing)
+ {
+ u->changing=0;
+ for(i=0;i<9;i++)
+ {
+ *d++=*s++;
+ }
+ }
+ else
+ {
+ s+=10;
+ p="say proxy:rec";
+ while(*p) *d++=*p++;
+ modhere=1;
+ }
+ }
+ else
+ {
+ *d++=*s++;
+ }
+ }
+ *d++=*s++; // trailing zero
+ if(modhere)
+ {
+ logx(LOG_DEBUG,"Stufftext modified: %s",d0);
+ modified=1;
+ }
+ break;
+ }
+ } break;
+ case 0x0A:
+ {
+ if(dump) logx(LOG_PACKETS,"angles\n",s);
+ DUP1; DUP1; DUP1;
+ } break;
+ case 0x0b: // FIRST packet from server
+ {
+ int i,a,b;
+ if(dump) logx(LOG_PACKETS,"serverdata\n",s);
+ u->donew=0; // do not send new anymore
+ a=DUP4;
+ b=DUP4;
+ DUPTEXT;
+ u->entity=1+DUP1;
+ if(u->entity&0x80) u->amspectator=1;
+ else u->amspectator=0;
+ u->amconnected=10;
+ u->entity&=0x7f;
+ DUPTEXT;
+ for(i=0;i<10;i++) DUP4;
+
+ // clear level description fields
+ {
+ int i;
+
+ u->time_reskinall=u->time+10000;
+ u->fakedisablecnt=100;
+ *u->msgline=0;
+ *u->lastpoint=0;
+ u->specmode=0;
+
+ u->locations=0;
+
+ *u->locname=0;
+ u->camps=0;
+ for(i=0;i<16;i++) u->campcnt[i]=1;
+ memset(u->camp,0,sizeof(u->camp));
+
+ u->models=0;
+ u->sounds=0;
+ u->cnt_sound=0;
+ u->cnt_model=0;
+ u->time_died=0;
+ u->changing=0;
+
+ u->explosionsound=0; // 0=use 1
+ u->grenademodel=0; // 0=add by 1
+ u->pack2model=0; // 0=add by 1
+
+ u->lastreportedloc=-1;
+
+ memset(u->playername,0,sizeof(uchar)*34*MAXPLAYERNAME);
+ memset(u->playerskin,0,sizeof(uchar)*34*MAXSKINNAME);
+ memset(u->playerteam,0,sizeof(int)*34);
+ memset(u->playerfps,0,sizeof(int)*34);
+ memset(u->playerteamtext,0,sizeof(uchar)*8*34);
+ memset(u->playerbcolor,0,sizeof(int)*34);
+ memset(u->playertcolor,0,sizeof(int)*34);
+ memset(u->playerfrags,0,sizeof(int)*34);
+ memset(u->playertime,0,sizeof(int)*34);
+ memset(u->playerproxy,0,sizeof(int)*34);
+ memset(u->playernear,0,sizeof(int)*34);
+ memset(u->playerglow,0,sizeof(int)*34);
+ memset(u->playeractive,0,sizeof(int)*34);
+ memset(u->playerpos,0,sizeof(int)*34*3);
+ memset(u->playerang,0,sizeof(int)*34*3);
+ memset(u->playerloc,0,sizeof(int)*34);
+ memset(u->playerlastloc,0,sizeof(int)*34);
+ u->lastenemymask=0;
+
+ memset(u->entitygib,0,sizeof(uchar)*MAXENTITIES);
+ memset(u->entityvisible,0,sizeof(uchar)*MAXENTITIES);
+ memset(u->entityname,0,sizeof(uchar *)*MAXENTITIES);
+ memset(u->entitypos,0,sizeof(int)*MAXENTITIES*3);
+ memset(u->entitymask,0,sizeof(int)*MAXENTITIES);
+
+ // clear user stats
+ cl_clearstats(u);
+ }
+
+ } break;
+ case 0x0c:
+ {
+ if(dump) logx(LOG_PACKETS,"lightstyle\n");
+ DUP1;
+ DUPTEXT;
+ } break;
+ case 0x0e:
+ {
+ int pl,fr;
+ if(dump) logx(LOG_PACKETS,"updatefrags\n");
+ pl=DUP1;
+ fr=DUP2;
+ u->playerfrags[pl+1]=fr;
+ } break;
+ case 0x10:
+ {
+ if(dump) logx(LOG_PACKETS,"stopsound\n");
+ DUP2;
+ } break;
+ case 0x13:
+ {
+ uchar *d0=d-1;
+ if(dump) logx(LOG_PACKETS,"damage\n");
+ DUP1;
+ DUP1;
+ DUP2; DUP2; DUP2;
+ if(u->fps_damage)
+ {
+ d=d0;
+ modified=1;
+ }
+ } break;
+ case 0x14:
+ {
+ if(dump) logx(LOG_PACKETS,"spawnstatic\n");
+ DUP1;
+ DUP1;
+ DUP1;
+ DUP1;
+ //
+ DUP2;
+ DUP1;
+ DUP2;
+ DUP1;
+ DUP2;
+ DUP1;
+ } break;
+ case 0x16:
+ {
+ int entity,modelindex,frame,colormap,skin;
+ int ang[3],pos[3];
+ Camp *ca;
+
+ entity=DUP2;
+ modelindex=DUP1;
+
+ if(dump) logx(LOG_PACKETS,"spawnbaseline %i (%s)\n",entity,u->model[modelindex]);
+
+ frame=DUP1;
+ colormap=DUP1;
+ skin=DUP1;
+ //
+ pos[0]=DUP2;
+ ang[0]=DUP1;
+ pos[1]=DUP2;
+ ang[1]=DUP1;
+ pos[2]=DUP2;
+ ang[2]=DUP1;
+
+ if(entity<MAXENTITIES)
+ {
+ u->entitypos[entity][0]=pos[0];
+ u->entitypos[entity][1]=pos[1];
+ u->entitypos[entity][2]=pos[2];
+ }
+
+ //printf("entity %03X: %s/%i %i %i %i ",entity,u->model[modelindex],skin,pos[0],pos[1],pos[2]);
+
+ ca=campadd(u,entity,pos,modelindex,skin);
+ if(ca && ca->category && ca->name[0]!='!')
+ {
+ u->entityname[entity]=ca->genericname;
+ u->entitymask[entity]=ca->category;
+ logx(LOG_DEBUG,"Camping %s (e:%03X pos:%i,%i,%i)\n",
+ ca->name,ca->entity,ca->pos[0],ca->pos[1],ca->pos[2]);
+ }
+ else
+ { // normal item
+ u->entityname[entity]=makeentityname(u,modelindex,skin,&u->entitymask[entity]);
+ }
+
+ if(ca && u->camps<MAXCAMP) u->camps++;
+ } break;
+ case 0x17:
+ {
+ int a;
+ uchar *d0=d-1;
+ if(dump) logx(LOG_PACKETS,"tempentity\n");
+ a=DUP1;
+ if(a==5 || a==6 || a==9)
+ {
+ DUP2;
+ DUP2; DUP2; DUP2;
+ DUP2; DUP2; DUP2;
+ }
+ else if(a==2 || a==12)
+ {
+ DUP1;
+ DUP2; DUP2; DUP2;
+ }
+ else if(a==3)
+ { // explosion
+ int x,y,z;
+ if(u->fps_explosion==1) d[-1]=4; // tarbaby
+ if(u->fps_explosion==2) d[-1]=11; // teleport
+ if(u->fps_explosion==3)
+ { // blood
+ d[-1]=12;
+ *d++=16; // 10 particles of blood
+ }
+ if(u->fps_explosion==5)
+ { // gunshot
+ d[-1]=2;
+ *d++=16; // 10 particles of gunshot
+ }
+ x=DUP2; y=DUP2; z=DUP2;
+ if(u->fps_explosion==4) d=d0;
+ if(u->fps_explosion)
+ {
+ *d++=0x06;
+ *d++=0x00;
+ *d++=0x00;
+ if(!u->explosionsound) *d++=1;
+ else *d++=u->explosionsound;
+ *d++=(x&255);
+ *d++=(x>>8);
+ *d++=(y&255);
+ *d++=(y>>8);
+ *d++=(z&255);
+ *d++=(z>>8);
+ modified=1;
+ }
+ }
+ else
+ {
+ DUP2; DUP2; DUP2;
+ }
+ } break;
+ case 0x18:
+ {
+ if(dump) logx(LOG_PACKETS,"pause\n");
+ DUP1;
+ } break;
+ case 0x1a:
+ {
+ int l,e=0;
+ char *p;
+ if(dump) logx(LOG_PACKETS,"centerprint: %s\n",formatqstring(s));
+
+ if(u->moveoverlay)
+ {
+ p=s;
+ while(*p)
+ {
+ if(*p=='\n') e++;
+ p++;
+ }
+ if(e<4) e=1;
+ else e=0;
+ }
+
+ l=strlen(s);
+ if(l<255 && u->overlayactive)
+ {
+ u->time_nextcamp=0; // force overlay update
+ u->time_lastoverlay=u->time;
+ if(e)
+ {
+ int i;
+ for(i=0;i<u->moveoverlay*2;i++)
+ {
+ u->overlay[i]='\n';
+ }
+ memcpy(u->overlay+i,s,l+1);
+ }
+ else memcpy(u->overlay,s,l+1);
+ SKIPTEXT;
+ d--;
+ modified=1;
+ }
+ else
+ {
+ if(e)
+ {
+ int i;
+ for(i=0;i<u->moveoverlay*2;i++)
+ {
+ *d++='\n';
+ }
+ modified=1;
+ }
+ DUPTEXT;
+ if(u->time_nextcamp<u->time+750) u->time_nextcamp=u->time+750;
+ }
+ } break;
+ case 0x1b:
+ {
+ if(dump) logx(LOG_PACKETS,"killedmonster\n");
+ } break;
+ case 0x1c:
+ {
+ if(dump) logx(LOG_PACKETS,"foundsecret\n");
+ } break;
+ case 0x1d:
+ {
+ if(dump) logx(LOG_PACKETS,"spawnstaticsound\n");
+ DUP2; DUP2; DUP2;
+ DUP1;
+ DUP1;
+ DUP1;
+ } break;
+ case 0x1e:
+ {
+ if(dump) logx(LOG_PACKETS,"intermission\n");
+ DUP2; DUP2; DUP2;
+ DUP1; DUP1; DUP1;
+ } break;
+ case 0x1f:
+ {
+ if(dump) logx(LOG_PACKETS,"finale\n");
+ DUPTEXT;
+ } break;
+ case 0x20:
+ {
+ if(dump) logx(LOG_PACKETS,"cdtrack\n");
+ DUP1;
+ } break;
+ case 0x21:
+ {
+ if(dump) logx(LOG_PACKETS,"sellscreen\n");
+ } break;
+ case 0x22:
+ {
+ if(dump) logx(LOG_PACKETS,"smallkick\n");
+ } break;
+ case 0x23:
+ {
+ if(dump) logx(LOG_PACKETS,"bigkick\n");
+ } break;
+ case 0x24:
+ {
+ if(dump) logx(LOG_PACKETS,"updateping\n");
+ DUP1;
+ DUP2;
+ } break;
+ case 0x25:
+ {
+ int pl,time;
+ float f;
+ if(dump) logx(LOG_PACKETS,"updateentertime\n");
+ pl=DUP1;
+ time=DUP4;
+ f=*(float *)&time;
+ u->playertime[pl+1]=(int)(f*1000.0)-u->time; // millisec
+ } break;
+ case 0x26:
+ {
+ int stat,value;
+ if(dump) logx(LOG_PACKETS,"updatestatint\n");
+ stat=DUP1;
+ value=DUP4;
+ cl_updatestat(u,stat,value);
+ } break;
+ case 0x27:
+ {
+ int e;
+ e=DUP2;
+ if(u->fps_muzzle==2 || (u->fps_muzzle==1 && e==u->entity))
+ { // remove flash
+ d-=3;
+ modified=1;
+ }
+ if(dump) logx(LOG_PACKETS,"muzzleflash\n");
+ } break;
+ case 0x28: // setinfo / userinfo
+ {
+ int entity;
+ static uchar name[MAXSTR];
+ static uchar team[MAXSTR];
+ static uchar skin[MAXSTR];
+ static uchar spec[MAXSTR];
+ static uchar specnext[MAXSTR];
+ static uchar proxy[MAXSTR];
+ static uchar topc[MAXSTR];
+ static uchar botc[MAXSTR];
+ static uchar xxxc[MAXSTR];
+ uchar *sskin;
+
+ if(dump) logx(LOG_PACKETS,"updateuserinfo\n");
+ entity=DUP1+1;
+ DUP4;
+
+ if(entity>=u->numplayers) u->numplayers=entity+1;
+
+ *name=0;
+ extractfield(name,s,"name\\");
+
+ *team=0;
+ extractfield(team,s,"team\\");
+
+ *spec=0;
+ extractfield(spec,s,"*spectator\\");
+
+ *specnext=0;
+ extractfield(specnext,s,"spectator\\");
+
+ *skin=0;
+ sskin=extractfield(skin,s,"skin\\");
+
+ *topc=0;
+ extractfield(topc,s,"topcolor\\");
+
+ *botc=0;
+ extractfield(botc,s,"bottomcolor\\");
+
+ *xxxc=0;
+ extractfield(xxxc,s,"teamcolor\\");
+
+ strcpy(u->playerproxy[entity],"-");
+
+ *proxy=0;
+ extractfield(proxy,s,"NFProxy\\");
+ if(*proxy)
+ {
+ proxy[15]=0;
+ sprintf(u->playerproxy[entity],"NFProxy %s",proxy);
+ }
+
+ *proxy=0;
+ extractfield(proxy,s,"Qizmo\\");
+ if(*proxy)
+ {
+ proxy[15]=0;
+ sprintf(u->playerproxy[entity],"Qizmo %s",proxy);
+ }
+
+ *proxy=0;
+ extractfield(proxy,s,"Cheapo\\");
+ if(*proxy)
+ {
+ proxy[15]=0;
+ sprintf(u->playerproxy[entity],"Cheapo %s",proxy);
+ }
+
+ logx(LOG_DEBUG,"user %i: skin<%s>\n",entity,skin);
+ if(strlen(skin)>16)
+ {
+ int num;
+
+ skin[16]=0;
+
+ while(s<sskin)
+ {
+ *d++=*s++;
+ }
+
+ num=16;
+ while(num--) *d++=*s++;
+ while(*s!='\\' && *s) s++;
+
+ while(*s) *d++=*s++;
+ *d++=*s++;
+
+ modified=1;
+
+// q_printf(u,"DEBUG: longskin\n");
+ logx(LOG_DEBUG,"user %i: longskin\n",entity);
+ }
+ else
+ {
+ DUPTEXT;
+ }
+
+ strncpy(u->playername[entity],name,MAXPLAYERNAME-1);
+ strncpy(u->playerteamtext[entity],team,7);
+ u->playerteam[entity]=teamint(team);
+ strncpy(u->playerskin[entity],skin,MAXSKINNAME-1);
+ u->playernear[entity]=0;
+
+ u->playertcolor[entity]=atoi(topc);
+ u->playerbcolor[entity]=atoi(botc);
+ u->playerxcolor[entity]=text2color(xxxc);
+
+ if(entity==u->entity) strncpy(u->name,name,MAXSTR-1);
+ if(entity==u->entity) u->team=teamint(team);
+ if(entity==u->entity) u->tcolor=u->playertcolor[entity];
+ if(entity==u->entity) u->bcolor=u->playerbcolor[entity];
+
+ if(*spec!='1' && *name)
+ {
+ u->playeractive[entity]=1;
+ skinoverride(u,entity);
+ }
+ else
+ {
+ u->playeractive[entity]=0;
+ }
+ logx(LOG_DEBUG,"user %i: name<%s> active<%i>\n",
+ entity,
+ u->playername[entity],
+ u->playeractive[entity]);
+ } break;
+ case 0x29:
+ {
+ int i;
+ if(dump) logx(LOG_PACKETS,"download\n");
+ i=DUP2;
+ DUP1;
+ DUPN(i);
+ } break;
+ case 0x2A: // 0x2a
+ {
+ int mask,mask2,ukb,fra,hidethis,modelindex,weaponframe,glow,pingx=0,ms=10;
+ int pl,pos[3],ang[3];
+ uchar *d0=d-1;
+ hidethis=0;
+ pl=1+DUP1; // player entity
+ if(pl>=u->numplayers) u->numplayers=pl+1;
+ u->playernear[pl]=8;
+ mask=DUP2;
+ // position
+ pos[0]=DUP2;
+ pos[1]=DUP2;
+ pos[2]=DUP2;
+ ang[0]=0;
+ ang[1]=0;
+ ang[2]=0;
+ // frame
+ fra=DUP1;
+ if(fra<LASTPLAYERFRAME)
+ {
+ if(playeranim[fra][0]=='d')
+ {
+ u->playernear[pl]=0; // dead player don't count
+ }
+ if(u->hideaxemen)
+ {
+ if(playeranim[fra][0]=='a' && pl!=u->entity)
+ {
+ hidethis=1;
+ }
+ }
+ }
+ // other stuff
+ if(mask&0x0001)
+ {
+ pingx=DUP1; // ping
+ }
+ if(mask&0x0002)
+ {
+ mask2=DUP1;
+ if(u->version>=23 && mask2==0)
+ { // weird
+ DUP1;
+ }
+ else
+ {
+ // angle
+ if(mask2&0x01) ang[0]=DUP2;
+ if(mask2&0x80) ang[1]=DUP2;
+ if(mask2&0x02) ang[2]=DUP2;
+ // speed
+ if(u->version>=23)
+ {
+ if(mask2&0x04) DUP2;
+ if(mask2&0x08) DUP2;
+ if(mask2&0x10) DUP2;
+ }
+ else
+ {
+ if(mask2&0x04) DUP1;
+ if(mask2&0x08) DUP1;
+ if(mask2&0x10) DUP1;
+ }
+ // flags
+ if(mask2&0x20)
+ {
+ DUP1; // +1=fire, +2=jump
+ }
+ // impulse
+ if(mask2&0x40)
+ {
+ DUP1;
+ }
+ // load
+ //if(mask2&0x80)
+ {
+ int fps;
+ ms=DUP1;
+ if(ms<10)
+ {
+ ms=10;
+ fps=100;
+ }
+ else fps=1000/ms;
+ u->playerfps[pl]=(u->playerfps[pl]+fps+1)/2;
+ }
+ }
+ }
+ else mask2=0;
+
+ if(mask&0x0004)
+ {
+ DUP2; // xspeed
+ }
+ if(mask&0x0008)
+ {
+ DUP2; // yspeed
+ }
+ if(mask&0x0010)
+ {
+ DUP2; // zspeed
+ }
+
+ if(mask&0x0020)
+ {
+ modelindex=DUP1; // modelindex
+ }
+ else modelindex=0;
+
+ if(mask&0x0040)
+ {
+ ukb=DUP1; // modelframe?
+ }
+ else ukb=0;
+
+ if(mask&0x0080)
+ { // powerup glow
+ int w;
+ w=glow=DUP1;
+ u->playerglow[pl]=w;
+ //logx(LOG_CONSOLE,"player %i weapon %02X\n",w);
+ if(u->fps_powerup==2 || (u->fps_powerup==1 && pl==u->entity))
+ {
+ modified=1;
+ d[-1]=0;
+ }
+ }
+ else
+ {
+ glow=u->playerglow[pl]=0;
+ }
+
+ /*
+ if(pl==u->entity)
+ {
+ if(glow==8)
+ {
+ if(!(u->power&ITEMS_FLAG))
+ {
+ u->power|=ITEMS_FLAG;
+ checkpowers(u);
+ }
+ }
+ else if(!glow)
+ {
+ if((u->power&ITEMS_FLAG))
+ {
+ u->power&=~ITEMS_FLAG;
+ checkpowers(u);
+ }
+ }
+ }
+ */
+
+ if(mask&0x0100)
+ {
+ weaponframe=DUP1; // weaponframe
+ }
+ else weaponframe=0;
+
+ if(mask&0x0200)
+ {
+ //??
+ }
+
+ if(dump)
+ {
+ if(mask&2)
+ {
+ logx(LOG_PACKETS,"player %i mask=%04X/%02X sz=%i: ",
+ pl,mask,mask2,d-d0);
+ }
+ else
+ {
+ logx(LOG_PACKETS,"player %i mask=%04X/xx sz=%i: ",
+ pl,mask,mask2,d-d0);
+ }
+ logx(LOG_PACKETS,"p0 p1 p2 frame ");
+ if(mask&1) logx(LOG_PACKETS,"ping(%i) ",pingx);
+ if(mask&2)
+ {
+ if(mask2&1) logx(LOG_PACKETS,"a0 ");
+ logx(LOG_PACKETS,"a1 ");
+ if(mask2&2) logx(LOG_PACKETS,"a2 ");
+ if(mask2&4) logx(LOG_PACKETS,"as0 ");
+ if(mask2&8) logx(LOG_PACKETS,"as1 ");
+ if(mask2&16) logx(LOG_PACKETS,"as2 ");
+ if(mask2&32) logx(LOG_PACKETS,"f/j ");
+ if(mask2&64) logx(LOG_PACKETS,"imp ");
+ if(mask2&128) logx(LOG_PACKETS,"load ");
+ }
+ if(mask&4) logx(LOG_PACKETS,"s0 ");
+ if(mask&8) logx(LOG_PACKETS,"s1 ");
+ if(mask&16) logx(LOG_PACKETS,"s2 ");
+ if(mask&32) logx(LOG_PACKETS,"model ");
+ if(mask&64) logx(LOG_PACKETS,"m.fr? ");
+ if(mask&128) logx(LOG_PACKETS,"glow ");
+ if(mask&256) logx(LOG_PACKETS,"w.fr ");
+ logx(LOG_PACKETS,"\n");
+ }
+
+ if(modelindex && strstr(u->model[modelindex],"eyes"))
+ u->playerglow[pl]|=0x100;
+ else
+ u->playerglow[pl]&=~0x100;
+
+ if(pl!=u->entity && u->playerteam[pl]!=u->team)
+ { // lastenemymask
+ int mask=0;
+ if(u->playerglow[pl]>0)
+ {
+ if(u->playerglow[pl]&0x100)
+ {
+ mask|=CAMP_R;
+ }
+ if(u->playerglow[pl]&0x40)
+ {
+ mask|=CAMP_Q;
+ }
+ if(u->playerglow[pl]&0x80)
+ {
+ mask|=CAMP_P;
+ }
+ }
+ if(mask)
+ {
+ u->lastenemymask=mask;
+ u->time_lastenemymask=u->time;
+ }
+ }
+
+ if(pl==u->entity)
+ {
+ if(u->showspeed)
+ {
+ float dx,dy,dz;
+ int s,n;
+ n=u->server_lastthis-u->speedlastthis;
+ if(n<1) n=1;
+ dx=(float)(u->pos[0]-pos[0]);
+ dy=(float)(u->pos[1]-pos[1]);
+ dz=(float)(u->pos[2]-pos[2]);
+ s=(int)(sqrt(dx*dx+dy*dy)/n)*1000/ms;
+ u->speedcnt=(u->speedcnt+1)&3;
+ u->speed[u->speedcnt]=s;
+ u->speedlastthis=u->server_lastthis;
+ }
+ u->pos[0]=pos[0];
+ u->pos[1]=pos[1];
+ u->pos[2]=pos[2];
+ }
+
+ u->playerpos[pl][0]=pos[0];
+ u->playerpos[pl][1]=pos[1];
+ u->playerpos[pl][2]=pos[2];
+ u->playerang[pl][0]=ang[0];
+ u->playerang[pl][1]=ang[1];
+ u->playerang[pl][2]=ang[2];
+ if(u->specmode && pl==u->specentity)
+ {
+ u->tr_postar[0]=pos[0];
+ u->tr_postar[1]=pos[1];
+ u->tr_postar[2]=pos[2];
+ u->tr_angtar[0]=ang[0];
+ u->tr_angtar[1]=ang[1];
+ u->tr_angtar[2]=ang[2];
+ }
+ if(u->specmode && (pl==u->entity || pl==u->specentity))
+ {
+ hidethis=1;
+ }
+
+ if(hidethis)
+ {
+ d=d0;
+ modified=1;
+ }
+ } break;
+ case 0x2B:
+ {
+ int nailcount,j;
+ if(dump) logx(LOG_PACKETS,"nails\n");
+ if(u->fps_nails>1)
+ {
+ char *d0=d;
+ int newcount=0;
+ int cnt=0;
+ modified=1;
+
+ nailcount=DUP1;
+ for(j=0;j<nailcount;j++)
+ {
+ cnt++;
+ if(cnt>=u->fps_nails) cnt=0;
+
+ if(!cnt)
+ {
+ s+=6;
+ continue;
+ }
+
+ newcount++;
+ DUP2;
+ DUP2;
+ DUP2;
+ }
+
+ *d0=newcount;
+ }
+ else
+ {
+ nailcount=DUP1;
+ for(j=0;j<nailcount;j++)
+ {
+ DUP2;
+ DUP2;
+ DUP2;
+ }
+ }
+ } break;
+ case 0x2C:
+ {
+ if(dump) logx(LOG_PACKETS,"choke\n");
+ u->choke=DUP1;
+ } break;
+ case 0x2D:
+ {
+ if(dump) logx(LOG_PACKETS,"modellist\n");
+ u->fakedisablecnt=100;
+ if(NEWQW) u->models=DUP1+1; // new22
+ else u->models=1;
+ while(*s)
+ {
+ if(u->models>0 && u->models<MAXMODELS)
+ {
+ if(u->model[u->models]) free(u->model[u->models]);
+ u->model[u->models]=mymalloc(strlen(s)+2);
+ logx(LOG_PACKETS,"- model %i = %s\n",u->models,s);
+ strcpy(u->model[u->models],s);
+ if(!strcmp(u->model[u->models],"progs/grenade.mdl"))
+ {
+ logx(LOG_DEBUG,"grenademodel=%i\n",u->models);
+ u->grenademodel=u->models;
+ }
+ if(!strcmp(u->model[u->models],"progs/g_rock2.mdl"))
+ {
+ logx(LOG_DEBUG,"pack2model=%i\n",u->models);
+ u->pack2model=u->models;
+ }
+ u->models++;
+ }
+ DUPTEXT;
+ }
+ if(u->models>MAXMODELS) u->models=MAXMODELS;
+ DUP1;
+ if(NEWQW) DUP1; // new22
+ } break;
+ case 0x2E:
+ {
+ if(dump) logx(LOG_PACKETS,"soundlist\n");
+ u->fakedisablecnt=100;
+ if(NEWQW) u->sounds=DUP1+1; // new22
+ else u->sounds=1;
+ while(*s)
+ {
+ if(u->sounds>0 && u->sounds<MAXSOUNDS)
+ {
+ if(u->sound[u->sounds]) free(u->sound[u->sounds]);
+ u->sound[u->sounds]=mymalloc(strlen(s)+2);
+ strcpy(u->sound[u->sounds],s);
+ logx(LOG_PACKETS,"- sound %i = %s\n",u->sounds,s);
+ if(!strcmp(u->sound[u->sounds],"weapons/r_exp3.wav"))
+ {
+ logx(LOG_DEBUG,"explosionsound=%i\n",u->sounds);
+ u->explosionsound=u->sounds;
+ }
+ u->sounds++;
+ }
+ DUPTEXT;
+ }
+ if(u->sounds>MAXSOUNDS) u->sounds=MAXSOUNDS;
+ DUP1;
+ if(NEWQW) DUP1; // new22
+ } break;
+ case 0x2F:
+ case 0x30:
+ {
+ int mask,entity,ef,index,modelindex,modelframe,modelskin = 0;
+ uchar *d0;
+ u->amconnected=10;
+ if(dump && cmd==0x2f) logx(LOG_PACKETS,"packetentities\n");
+ if(dump && cmd==0x30) logx(LOG_PACKETS,"packetentities(delta)\n");
+ if(cmd==0x30)
+ {
+ index=DUP1;
+ }
+ else
+ {
+ memset(u->entityvisible,0,sizeof(u->entityvisible));
+ }
+ for(;;)
+ {
+ int wasvisible,isvisible;
+ int oldpos[3];
+ int newobject=1;
+ d0=d;
+
+ mask=DUP2;
+ modelindex=0;
+ modelframe=0;
+ ef=0;
+ if(!mask) break;
+ entity=mask&0x1ff;
+ mask&=0xfe00;
+
+ if(mask&0x8000) mask|=DUP1;
+
+ wasvisible=u->entityvisible[entity];
+ if(mask&0x4000)
+ { // hide
+ u->entityvisible[entity]=0;
+ }
+ else
+ {
+ u->entityvisible[entity]=1;
+ }
+ isvisible=u->entityvisible[entity];
+
+ if(mask&0x0004)
+ {
+ modelindex=DUP1; // modelindex
+ if(u->fps_rocket && !strcmp(u->model[modelindex],"progs/missile.mdl"))
+ {
+ if(u->grenademodel<=0) d[-1]++;
+ else d[-1]=u->grenademodel;
+ modified=1;
+ }
+ }
+ if(mask&0x2000) modelframe=DUP1; // frame
+ if(mask&0x0008) DUP1; // colormap
+ if(mask&0x0010) modelskin=DUP1; // skin
+ if(mask&0x0020)
+ {
+ ef=DUP1; // effects
+ }
+
+ if(u->fps_gib && entity<MAXENTITIES && modelindex && cmd==0x30)
+ {
+ u->entitygib[entity]=0;
+
+ if(u->fps_gib==1)
+ { // also remove gibs
+ if(strstr(u->model[modelindex],"progs/gib")) u->entitygib[entity]=1;
+ }
+
+ { // remove bodies
+ if(strstr(u->model[modelindex],"progs/h_player")) u->entitygib[entity]=1;
+ if(strstr(u->model[modelindex],"progs/player")) u->entitygib[entity]=1;
+ }
+ }
+
+#if 0
+ if(isvisible && u->entityname[entity] && !strcmp(u->entityname[entity],"pack"))
+ {
+ modified=1;
+
+ if(mask&0x0020) d--; // remove old effect if present
+ // make sure the secondary mask is there
+ if(!(mask&0x8000))
+ {
+ uchar *p=d;
+ while(p>d0+2)
+ {
+ p[1]=p[0];
+ p--;
+ }
+ p[0]=0x20;
+ d0[1]|=0x80;
+ d++;
+ }
+ // add the mask
+ d0[2]|=0x20;
+
+ *d++=0x10; // add grenade effect
+
+ if(!wasvisible) q_print(u,"<pack appeared>\n");
+ }
+#endif
+
+ if(!wasvisible &&
+ (mask&(0x0004+0x0200+0x0400+0x0800))==(0x0004+0x0200+0x0400+0x0800))
+ {
+ newobject=1;
+ oldpos[0]=u->entitypos[entity][0];
+ oldpos[1]=u->entitypos[entity][1];
+ oldpos[2]=u->entitypos[entity][2];
+ }
+ else newobject=0;
+
+ if(mask&0x0200) u->entitypos[entity][0]=DUP2; // origin x
+ if(mask&0x0001) DUP1; // angle x
+ if(mask&0x0400) u->entitypos[entity][1]=DUP2; // origin y
+ if(mask&0x1000) DUP1; // angle y
+ if(mask&0x0800) u->entitypos[entity][2]=DUP2; // origin z
+ if(mask&0x0002) DUP1; // angle z
+
+ if(oldpos[0]==u->entitypos[entity][0] &&
+ oldpos[1]==u->entitypos[entity][1] &&
+ oldpos[2]==u->entitypos[entity][2]) newobject=0;
+
+ if(newobject)
+ {
+ u->entityname[entity]=makeentityname(u,modelindex,modelskin,&u->entitymask[entity]);
+
+ // determine pack owner (if model & pos given so pack is new)
+ if(u->classifypacks && !strcmp(u->entityname[entity],"pack"))
+ {
+ /*
+ if(oldpos[0]!=u->entitypos[entity] ||
+ oldpos[0]!=u->entitypos[entity] ||
+ oldpos[0]!=u->entitypos[entity])
+ */
+ { // pack is not in same pos as last time, good
+ if(packlistnum<u->numplayers)
+ {
+ packlist[packlistnum++]=entity;
+ }
+ }
+ }
+ }
+
+ if(dump)
+ {
+ logx(LOG_PACKETS,"entity %03X mask=%04X sz=%i: ",entity,mask,d-d0);
+ if(mask&0x8000) logx(LOG_PACKETS,"mask2 ");
+ if(mask&0x4000) logx(LOG_PACKETS,"(hide) ");
+ if(mask&0x0004) logx(LOG_PACKETS,"model ");
+ if(mask&0x2000) logx(LOG_PACKETS,"m.fr ");
+ if(mask&0x0008) logx(LOG_PACKETS,"cmap ");
+ if(mask&0x0010) logx(LOG_PACKETS,"skin ");
+ if(mask&0x0020) logx(LOG_PACKETS,"eff ");
+ if(mask&0x0200) logx(LOG_PACKETS,"p0 ");
+ if(mask&0x0001) logx(LOG_PACKETS,"a0 ");
+ if(mask&0x0400) logx(LOG_PACKETS,"p1 ");
+ if(mask&0x1000) logx(LOG_PACKETS,"a1 ");
+ if(mask&0x0800) logx(LOG_PACKETS,"p2 ");
+ if(mask&0x0002) logx(LOG_PACKETS,"a2 ");
+ logx(LOG_PACKETS,"\n");
+ }
+
+ if(u->fps_gib && u->entitygib[entity] && cmd==0x30)
+ {
+ int mask;
+ mask=entity|0x4000;
+ d=d0;
+ *d++=mask&255;
+ *d++=mask>>8;
+ modified=1;
+ }
+ }
+ } break;
+ case 0x31: // new22
+ { // maxspeed
+ if(dump) logx(LOG_PACKETS,"new maxspeed\n");
+ DUP4;
+ } break;
+ case 0x32: // new22
+ { // ?
+ int a;
+ a=DUP4;
+ if(dump) logx(LOG_PACKETS,"new unknown (ctf) %08X\n",a);
+ } break;
+ case 0x33: // new22
+ {
+ int entity;
+ static char attr[80];
+ static char value[80];
+ uchar *d0;
+ entity=1+DUP1; // player?
+ strcpy(attr,s);
+ DUPTEXT;
+ d0=d;
+ strcpy(value,s);
+ DUPTEXT;
+ if(dump) logx(LOG_PACKETS,"new setinfo %s=%s\n",attr,value);
+ if(!strcmp(attr,"team"))
+ {
+ strncpy(u->playerteamtext[entity],value,7);
+ u->playerteam[entity]=teamint(value);
+ if(entity==u->entity)
+ {
+ u->time_reskinall=u->time+500;
+ }
+ else skinoverride(u,entity);
+ }
+ else if(!strcmp(attr,"skin"))
+ {
+ if(strlen(value)>16)
+ {
+ value[16]=0;
+ d0[16]=0;
+ d=d0+17;
+ modified=1;
+ logx(LOG_DEBUG,"user %i: longskin1\n",entity);
+ }
+ strncpy(u->playerskin[entity],value,MAXSKINNAME-1);
+ skinoverride(u,entity);
+ }
+ else if(!strcmp(attr,"topcolor"))
+ {
+ u->playertcolor[entity]=atoi(value);
+ if(entity==u->entity)
+ {
+ u->tcolor=atoi(value);
+ }
+ skinoverride(u,entity);
+ }
+ else if(!strcmp(attr,"bottomcolor"))
+ {
+ u->playerbcolor[entity]=atoi(value);
+ if(entity==u->entity)
+ {
+ u->bcolor=atoi(value);
+ }
+ skinoverride(u,entity);
+ }
+ else if(!strcmp(attr,"teamcolor"))
+ {
+ u->playerxcolor[entity]=text2color(value);
+ skinoverride(u,entity);
+ }
+ else if(!strcmp(attr,"spectator"))
+ {
+ }
+ else if(!strcmp(attr,"Cheapo"))
+ {
+ if(!*value)
+ {
+ strcpy(u->playerproxy[entity],"-");
+ }
+ else
+ {
+ value[15]=0;
+ sprintf(u->playerproxy[entity],"Cheapo %s",value);
+ }
+ }
+ else if(!strcmp(attr,"name"))
+ {
+ strncpy(u->playername[entity],value,MAXPLAYERNAME-1);
+ }
+#ifdef IDENT
+ else if(!strcmp(attr,"#"))
+ {
+ gotinfomessage(u,value,entity);
+ }
+#endif
+ } break;
+ case 0x34: // new22
+ {
+ char *attr,*value;
+ if(dump) logx(LOG_PACKETS,"new serverinfo/userinfo?\n");
+
+ tmpbuf[0]='f'+10;
+ tmpbuf[1]='p'+11;
+ tmpbuf[2]='d'+12;
+ tmpbuf[3]=0;
+
+ attr=s;
+ DUPTEXT;
+ value=s;
+ DUPTEXT;
+
+ if(!strcmp(attr,"deathmatch"))
+ {
+ dmmchange(u,atoi(value));
+ }
+
+ tmpbuf[3]=0;
+ tmpbuf[2]-=12;
+ tmpbuf[1]-=11;
+ tmpbuf[0]-=10;
+
+ if(!strcmp(attr,tmpbuf))
+ {
+ u->fpd=atoi(value)|litemode;
+ u->fpdcopy=atoi(value)|litemode;
+#ifdef TIMERS
+ if(u->notimers) u->fpd|=FPD_TIMERS;
+ if(u->notimers) u->fpdcopy|=FPD_TIMERS;
+#endif
+ reportfpd(u,u->fpd);
+ }
+ } break;
+ case 0x35: // new23
+ {
+ DUP1; // player?
+ DUP1; // ploss
+ } break;
+ default:
+ { // unrecognized data, bailout (will cause parse error message)
+ s=s_end;
+ modified=0;
+ } break;
+ }
+ }
+
+#ifdef RLPACK
+ if(packlistnum)
+ {
+ int i,entity;
+ for(i=0;i<packlistnum;i++)
+ {
+ int owner=0;
+ entity=packlist[i];
+#ifdef RLPACKDEBUG
+ q_print(u,"<checkrlpack>\n");
+#endif
+ {
+ // find out whos pack
+ int ownerd=999999999;
+ int i,d;
+
+ for(i=0;i<=32;i++) if(u->playeractive[i] && u->time<u->time_playerdied[i]+1000)
+ {
+ d =abs(u->entitypos[entity][0]-u->playerpos[i][0]);
+ d+=abs(u->entitypos[entity][1]-u->playerpos[i][1]);
+ d+=abs(u->entitypos[entity][2]-u->playerpos[i][2]);
+ if(d<ownerd)
+ {
+ owner=i;
+ ownerd=d;
+ }
+ }
+ }
+ if(owner)
+ {
+#ifdef RLPACKDEBUG
+ char msg[80];
+ sprintf(msg,"<%s's pack, rtime %i>\n",
+ u->playername[owner],u->time-u->time_playerrocket[owner]);
+ q_print(u,msg);
+#endif
+ if(u->time-u->time_playerrocket[owner]<700)
+ {
+ char msg[64];
+ sprintf(msg,"proxy: rl-pack (%s)\n",u->playername[owner]);
+ q_print(u,msg);
+ u->entityname[entity]="rl-pack";
+ }
+ }
+ }
+ }
+#endif
+
+ if(u->specmode && u->specentity)
+ { // track
+ int a,i;
+ if(dump)
+ {
+ logx(LOG_DEBUG,"obs: %02X pos: %i %i %i angles: %i %i %i \n",
+ u->entity,
+ u->tr_postar[0],
+ u->tr_postar[1],
+ u->tr_postar[2],
+ u->tr_angtar[0],
+ u->tr_angtar[1],
+ u->tr_angtar[2]);
+ }
+
+ // smoothing
+ for(i=0;i<3;i++)
+ {
+ a=u->tr_pos[i]-u->tr_postar[i]; if(a<0) a=-a;
+ if(a>512 ) u->tr_pos[i]=u->tr_postar[i];
+ else u->tr_pos[i]=(u->tr_pos[i]+u->tr_postar[i])/2;
+ a=u->tr_ang[i]-u->tr_angtar[i]; if(a<0) a=-a;
+ if(a>8000) u->tr_ang[i]=u->tr_angtar[i];
+ else u->tr_ang[i]=(u->tr_ang[i]+u->tr_angtar[i])/2;
+ }
+
+ modified=1;
+ // create angles command from server
+ *d++=0x0a;
+ *d++=u->tr_ang[0]>>8;
+ *d++=u->tr_ang[1]>>8;
+ *d++=u->tr_ang[2]>>8;
+ // create player pos command from server
+ *d++=0x2a;
+ *d++=u->entity-1;
+ // mask
+ *d++=0;
+ *d++=0;
+ // position
+ *d++=u->tr_pos[0]&255;
+ *d++=u->tr_pos[0]>>8;
+ *d++=u->tr_pos[1]&255;
+ *d++=u->tr_pos[1]>>8;
+ *d++=u->tr_pos[2]&255;
+ *d++=u->tr_pos[2]>>8;
+ // frametime
+ *d++=0x1f;
+ // create a specating command to client
+ u->appendclient[u->appendclientbytes++]=0x06;
+ u->appendclient[u->appendclientbytes++]=u->tr_postar[0]&255;
+ u->appendclient[u->appendclientbytes++]=u->tr_postar[0]>>8;
+ u->appendclient[u->appendclientbytes++]=u->tr_postar[1]&255;
+ u->appendclient[u->appendclientbytes++]=u->tr_postar[1]>>8;
+ u->appendclient[u->appendclientbytes++]=u->tr_postar[2]&255;
+ u->appendclient[u->appendclientbytes++]=u->tr_postar[2]>>8;
+ }
+
+ if(modified)
+ {
+ logx(LOG_PACKETS,"Packet MODIFIED!\n");
+ u->dumpsentpacket=1;
+ }
+ if(!modified && (bytes!=d-outbuf || memicmp(buffer+8,outbuf+8,bytes-8)))
+ {
+ u->noappendnow=2;
+ logx(LOG_CONSOLE,"Packet parse error (saved to parse.log)\n",cmd);
+ logx(LOG_DEBUG,"last command %02X at %03X\n",cmd,cmdpos);
+ saveparseerror("<%02X@server.%04X>",cmdpos,buffer,bytes);
+ memcpy(outbuf,buffer,bytes);
+ *outbytes=bytes;
+ sprintf(buf,"proxy: parsing error [s%02X]\n",lastcmd);
+ q_print(u,buf);
+ if(u->parsecount>100)
+ {
+ u->safemode=1;
+#if defined(__APPLE__) || defined(MACOSX)
+ sprintf(buf,"proxy: packet parsing disabled\n");
+#else
+ sprintf(buf,"proxy: packet parsing disabled\n",cmd);
+#endif /* __APPLE__ ||ÊMACOSX */
+ q_print(u,buf);
+ q_print(u,buf);
+ q_print(u,buf);
+ }
+ else
+ u->parsecount+=10;
+ return;
+ }
+ else
+ {
+ // set size
+ *outbytes=d-outbuf;
+ if(u->parsecount<0)
+ u->parsecount=0;
+ else
+ u->parsecount--;
+ }
+}
+
+int handleclient(User *u,uchar *buffer,int bytes,uchar *outbuf,int *outbytes)
+{
+ unsigned int *p=(unsigned int *)buffer;
+ int duptmp;
+ uchar buf[MAXSTR];
+ uchar *s,*s_end,*d;
+ uchar *s_beg,*d_beg;
+ int cmd,cmdpos = 0;
+ int modified=0;
+ int dump=loglevel>=LOG_PACKETS;
+ int simplepacket=1;
+ int lastcmd = 0;
+
+ if(u->safemode)
+ {
+ memcpy(outbuf,buffer,bytes);
+ *outbytes=bytes;
+ return(0);
+ }
+
+ if(*p==0xffffffff)
+ {
+ memcpy(outbuf,buffer,bytes);
+ *outbytes=bytes;
+ return(0);
+ }
+
+ s=buffer;
+ s_end=buffer+bytes;
+ d=outbuf;
+ s_beg=s;
+ d_beg=d;
+ // copy headers
+ DUPN(8);
+ // new22
+ if(NEWQW)
+ {
+ *d++=u->response_b&255;
+ *d++=u->response_b>>8;
+ u->clientident=shortat(s);
+ s+=2;
+ }
+ else u->clientident=-1;
+ // copy rest
+ cmd=0xff;
+ while(s<s_end)
+ {
+ if(dump) logx(LOG_PACKETS,"%02X@%02X: ",*s,s-buffer);
+ lastcmd=cmd;
+ cmd=DUP1;
+ cmdpos=s-buffer;
+ if(cmd!=0x03 && cmd!=0x05) simplepacket=0;
+ switch(cmd)
+ {
+ case 0x04:
+ {
+ if(dump) logx(LOG_PACKETS,"servercmd: %s\n",formatqstring(s));
+
+ /*
+ {
+ sprintf(tmpbuf,"servercmd: %s\n",formatqstring(s));
+ q_print(u,tmpbuf);
+ }
+ */
+
+ if(!memcmp(s,"cheapolink",5))
+ {
+ u->linkedclient=1;
+ // remove command from server stream
+ modified=1;
+ d--;
+ // skip from source
+ SKIPTEXT;
+ }
+ else if(!memcmp(s,"say .",5))
+ {
+ q_command(u,s+4);
+ // remove command from server stream
+ modified=1;
+ d--;
+ // skip from source
+ SKIPTEXT;
+ }
+ else if(!memcmp(s,"say \".",6))
+ {
+ q_command(u,s+5);
+ // remove command from server stream
+ modified=1;
+ d--;
+ // skip from source
+ SKIPTEXT;
+ }
+ else if(!memcmp(s,"say !",5))
+ {
+ // remove command from server stream
+ modified=1;
+ d--;
+ // skip from source
+ SKIPTEXT;
+ q_print(u,"Ignored !-command\n");
+ }
+ else if(!memcmp(s,u->proxysay,strlen(u->proxysay)))
+ {
+ int l=strlen(u->proxysay);
+ s[l-1]='.';
+ q_command(u,s+l-1);
+ // remove command from server stream
+ modified=1;
+ d--;
+ // skip from source
+ SKIPTEXT;
+ }
+ else if(!memcmp(s,u->proxysay2,strlen(u->proxysay2)))
+ {
+ int l=strlen(u->proxysay2);
+ s[l-1]='.';
+ q_command(u,s+l-1);
+ // remove command from server stream
+ modified=1;
+ d--;
+ // skip from source
+ SKIPTEXT;
+ }
+ else if(!memcmp(s,"say ",4))
+ {
+ if(floodprot(u))
+ {
+ d--;
+ SKIPTEXT;
+ modified=1;
+ break;
+ }
+
+ if(u->state==STATE_HERE && !u->linkedserver)
+ {
+ int i;
+ // send saying to all other users in proxy
+ sprintf(tmpbuf,"[[%s]]: %s\n",u->name,formatqstring(s+4));
+ //strxor80(tmpbuf);
+ for(i=0;i<usernum;i++) if(user[i].state==STATE_HERE)
+ {
+ q_print(user+i,tmpbuf);
+ }
+ DUPTEXT;
+ }
+ else
+ {
+ /*
+ Userinfo ui;
+ userinfofromuser(&ui,u);
+ modified|=handlesaying(u,&ui,&d,&s);
+ */
+ modified|=funnyconvert(d,s,3);
+ d+=strlen(d)+1;
+ s+=strlen(s)+1;
+ }
+ }
+ else if(!memcmp(s,"say_team ",9))
+ {
+ if(floodprot(u))
+ {
+ d--;
+ SKIPTEXT;
+ modified=1;
+ break;
+ }
+
+ {
+ char *d0=d;
+ int m;
+ Userinfo ui;
+ userinfofromuser(&ui,u);
+ m=handlesaying(u,&ui,&d,&s);
+ if(m) modified|=1;
+ if(m&2)
+ {
+ fixuphidemsg(u,d0);
+ d=d0+strlen(d0)+1;
+ }
+ }
+ }
+ else if(!memcmp(s,"drop",4))
+ {
+ q_command(u,"!drop");
+ DUPTEXT;
+ }
+ else if(!memcmp(s,"ptrack",6))
+ { // spectator change
+ if(s[7]>32) u->client_spectrack=1;
+ else u->client_spectrack=0;
+ DUPTEXT;
+ }
+ else if(!memcmp(s,"begin",5))
+ { // game begin
+ int a;
+ u->time_reskinall=u->time+3000;
+ sscanf(s,"begin %i",&a);
+ u->lastbegin=a;
+ DUPTEXT;
+ }
+ else if(!memcmp(s,"soundlist",9))
+ {
+ int a,b;
+ sscanf(s,"soundlist %i %i",&a,&b);
+ if(b==0) u->cnt_sound++;
+ logx(LOG_DEBUG,"client requests soundlist %i %i\n",a,b);
+ DUPTEXT;
+ }
+ else if(!memcmp(s,"modellist",9))
+ {
+ int a,b;
+ sscanf(s,"modellist %i %i",&a,&b);
+ if(b==0) u->cnt_model++;
+ logx(LOG_DEBUG,"client requests modellist %i %i\n",a,b);
+ DUPTEXT;
+ }
+ else if(!memcmp(s,"setinfo",5))
+ {
+ uchar attr[MAXSTR],value[MAXSTR];
+ uchar *p,*sorg;
+ int endmark,change=0,remove=0;
+
+ sorg=s;
+ while(*s>32) s++;
+ while(*s<=32 && *s) s++;
+
+ *attr=0;
+ *value=0;
+
+ // attribute
+ p=attr;
+ if(*s=='\"')
+ {
+ s++;
+ endmark='\"';
+ }
+ else endmark=' ';
+ while(*s && *s!=endmark && p<attr+60)
+ {
+ *p++=*s++;
+ }
+ if(*s) s++;
+ *p=0;
+
+ while(*s<=32 && *s) s++;
+
+ // value
+ p=value;
+ if(*s=='\"')
+ {
+ s++;
+ endmark='\"';
+ }
+ else endmark=' ';
+ while(*s && *s!=endmark && p<value+60)
+ {
+ *p++=*s++;
+ }
+ if(*s) s++;
+ *p=0;
+
+ logx(LOG_DEBUG,"Setinfo <%s> <%s> s=%02X d=%02X\n",
+ attr,value,s-s_beg,d-d_beg);
+
+ if(!stricmp(attr,"Cheapo"))
+ { // do not allow changing of cheapo setinfo
+#ifndef PROMODE
+ remove=1;
+#endif
+ }
+ else if(!stricmp(attr,"name"))
+ { // fake name helper
+ if(funnyconvert(value,value,0))
+ {
+ q_print(u,"Name containing $ or # modified.\n");
+ change=1;
+ }
+ }
+ else if(!stricmp(attr,"teamcolor"))
+ {
+ u->playerxcolor[u->entity]=text2color(value);
+ skinoverride(u,u->entity);
+ }
+
+ if(!remove) cl_setinfo(u,attr,value);
+
+ if(remove)
+ {
+ d--;
+
+ s=sorg;
+ SKIPTEXT;
+
+ modified=1;
+ }
+ else if(change)
+ {
+ p="setinfo \"";
+ while(*p) *d++=*p++;
+ p=attr;
+ while(*p) *d++=*p++;
+ p="\" \"";
+ while(*p) *d++=*p++;
+ p=value;
+ while(*p) *d++=*p++;
+ p="\"";
+ while(*p) *d++=*p++;
+ *d++=0xA;
+ *d++=0;
+
+ s=sorg;
+ SKIPTEXT;
+
+ modified=1;
+ }
+ else
+ {
+ s=sorg;
+ DUPTEXT;
+ }
+ }
+ else
+ {
+ DUPTEXT;
+ }
+ } break;
+ case 0x05:
+ {
+ if(dump) logx(LOG_PACKETS,"sequence(?)\n");
+ DUP1;
+ } break;
+ case 0x06:
+ {
+ if(dump) logx(LOG_PACKETS,"spectator(?)\n");
+ DUP2; DUP2; DUP2;
+ } break;
+ case 0x03:
+ {
+ int i,sx,sy,sz,sp;
+ uchar *d0,*d1;
+ d0=d;
+ if(NEWQW) DUP1; // new22, crc
+ u->impulsehistory[0]=u->impulsehistory[1];
+ u->impulsehistory[1]=u->impulsehistory[2];
+ u->impulsehistory[2]=0;
+ if(u->version<=22)
+ {
+ for(i=0;i<3;i++)
+ {
+ int command,tilt=0,yaw=0,roll=0,flag,impulse;
+ impulse=0;
+ command=DUP1;
+ if(dump)
+ {
+ if(i!=0) logx(LOG_PACKETS,"--@--: ");
+ logx(LOG_PACKETS,"movement(%i): mask:%02X ",i,command);
+ }
+ sx=sy=sz=sp=0;
+ if(command&0x01) tilt=DUP2;
+ yaw=DUP2;
+ if(command&0x02) roll=DUP2;
+ if(i==0)
+ {
+ u->ang[0]=tilt;
+ u->ang[1]=yaw;
+ u->ang[2]=roll;
+ }
+ if(command&0x04) sx=DUP1; // speed x
+ if(command&0x08) sy=DUP1; // speed y
+ if(command&0x10) sz=DUP1; // speed z
+
+ if(command&0x20)
+ {
+ flag=DUP1; // flag
+ if(dump) logx(LOG_PACKETS,"flag:%02X ",flag);
+ if(!i)
+ {
+ if((flag&2) && !(u->lastflag&2) && u->specmode)
+ { // jump
+ spectatenext(u,NULL);
+ }
+ if((flag&1) && u->specmode)
+ { // fire
+ u->specmode=0;
+ q_print(u,"Tracking disabled.\n");
+ }
+ u->lastflag=flag;
+ }
+ if(u->specmode)
+ {
+ if(flag&2)
+ {
+ d[-1]&=~2;
+ modified=1;
+ }
+ }
+ }
+ else
+ {
+ if(!i) u->lastflag=0;
+ }
+ if(command&0x40)
+ {
+ impulse=DUP1; // impulse
+ if(dump) logx(LOG_PACKETS,"impulse:%02X ",impulse);
+ if(i==2 && impulse)
+ {
+ if(modifyimpulse(u,&impulse))
+ { // modified
+ if(dump) logx(LOG_PACKETS,"=>%02X ",impulse);
+ d[-1]=impulse;
+ modified=1;
+ u->impulsehistory[2]=impulse;
+ }
+ else
+ { // not modified
+ u->impulsehistory[2]=0;
+ }
+ }
+ else if(u->impulsehistory[i])
+ {
+ if(dump) logx(LOG_PACKETS,"=>%02X ",u->impulsehistory[i]);
+ d[-1]=u->impulsehistory[i];
+ modified=1;
+ }
+ }
+ if(command&0x80)
+ {
+ sp=DUP1;
+ {
+ int ms=sp;
+ int fps;
+ if(ms<10) fps=100;
+ else fps=1000/ms;
+ u->playerfps[u->entity]=(u->playerfps[u->entity]+fps+1)/2;
+ }
+ if(dump) logx(LOG_PACKETS,"time:%i ",sp);
+ /* speed cheat
+ modified=1;
+ d[-1]*=2;
+ */
+ }
+ if(dump) logx(LOG_PACKETS,"\n");
+ }
+ }
+ else
+ {
+ if(u->version>=24)
+ {
+ DUP1; // ploss
+ }
+ for(i=0;i<3;i++)
+ {
+ int command,tilt=0,yaw=0,roll=0,flag,impulse;
+ char *cmd0;
+ impulse=sx=sy=sz=sp=0;
+ cmd0=d;
+ command=DUP1;
+ if(dump)
+ {
+ if(i!=0) logx(LOG_PACKETS,"--@--: ");
+ logx(LOG_PACKETS,"movement(%i): mask:%02X ",i,command);
+ }
+ if(command&0x01) tilt=DUP2;
+ if(command&0x80) yaw =DUP2;
+ if(command&0x02) roll=DUP2;
+ if(i==0)
+ {
+ u->ang[0]=tilt;
+ u->ang[1]=yaw;
+ u->ang[2]=roll;
+ }
+
+ if(command&0x04)
+ { // forward
+ sx=DUP2;
+ }
+ if(command&0x08)
+ { // strafe
+ sy=DUP2;
+ }
+ if(command&0x10)
+ { // jump
+ sz=DUP2;
+ }
+
+ if(command&0x20)
+ {
+ flag=DUP1; // flag
+ if(dump) logx(LOG_PACKETS,"flag:%02X ",flag);
+ if(!i)
+ {
+ if((flag&2) && !(u->lastflag&2) && u->specmode)
+ { // jump
+ spectatenext(u,NULL);
+ }
+ if((flag&1) && u->specmode)
+ { // fire
+ u->specmode=0;
+ q_print(u,"Tracking disabled.\n");
+ }
+ u->lastflag=flag;
+ }
+ if(u->specmode)
+ {
+ if(flag&2)
+ {
+ d[-1]&=~2;
+ modified=1;
+ }
+ }
+ }
+ else
+ {
+ if(!i) u->lastflag=0;
+ }
+
+ if(command&0x40)
+ {
+ impulse=DUP1; // impulse
+ if(dump) logx(LOG_PACKETS,"impulse:%02X ",impulse);
+ if(i==2 && impulse)
+ {
+ if(modifyimpulse(u,&impulse))
+ { // modified
+ if(dump) logx(LOG_PACKETS,"=>%02X ",impulse);
+ d[-1]=impulse;
+ modified=1;
+ u->impulsehistory[2]=impulse;
+ }
+ else
+ { // not modified
+ u->impulsehistory[2]=0;
+ }
+ }
+ else if(u->impulsehistory[i])
+ {
+ if(dump) logx(LOG_PACKETS,"=>%02X ",u->impulsehistory[i]);
+ d[-1]=u->impulsehistory[i];
+ modified=1;
+ }
+ }
+ {
+ sp=DUP1;
+ {
+ int ms=sp;
+ int fps;
+ if(ms<10) fps=100;
+ else fps=1000/ms;
+ u->playerfps[u->entity]=(u->playerfps[u->entity]+fps+1)/2;
+ }
+ if(dump) logx(LOG_PACKETS,"time:%i ",sp);
+ }
+
+ if(dump) logx(LOG_PACKETS,"\n");
+ }
+ }
+
+ d1=d;
+
+#if 0
+ logx(LOG_VERBOSE,"Client movedata check: packet %i/%i real %02X calculated %02X\n",
+ *((int *)(buffer+0)) & 0x7fffffff,
+ *((int *)(buffer+4)) & 0x7fffffff,
+ d0[0],checksum(buffer,d0+1,d1-d0-1));
+#endif
+
+ if(NEWQW && modified)
+ { // recalculate packet checksum
+ *d0=checksum(buffer,d0+1,d1-d0-1);
+ }
+ } break;
+ default:
+ { // unrecognized data, bailout (will cause parse error message)
+ s=s_end;
+ modified=0;
+ } break;
+ }
+ }
+
+ if(modified)
+ {
+ logx(LOG_PACKETS,"Packet MODIFIED!\n");
+ u->dumpsentpacket=1;
+ }
+ if(!modified && (bytes!=d-outbuf || memicmp(buffer+10,outbuf+10,bytes-10)))
+ {
+ lognpackets=1;
+ oldloglevel=loglevel;
+ loglevel=LOG_PACKETS;
+
+ u->noappendnow=2;
+ logx(LOG_CONSOLE,"Packet parse error (saved to parse.log)\n",cmd);
+ logx(LOG_DEBUG,"last command %02X at %03X\n",cmd,cmdpos);
+ saveparseerror("<%02X@client.%04X>",cmdpos,buffer,bytes);
+ memcpy(outbuf,buffer,bytes);
+ *outbytes=bytes;
+ sprintf(buf,"proxy: parsing error [c%02X]\n",lastcmd);
+ q_print(u,buf);
+ if(u->parsecount>100)
+ {
+ u->safemode=1;
+#if defined(__APPLE__) || defined(MACOSX)
+ sprintf(buf,"proxy: packet parsing disabled\n");
+#else
+ sprintf(buf,"proxy: packet parsing disabled\n",cmd);
+#endif /* __APPLE__ ||ÊMACOSX */
+ q_print(u,buf);
+ q_print(u,buf);
+ q_print(u,buf);
+ }
+ else
+ u->parsecount+=10;
+ return(0);
+ }
+ else
+ {
+ // set size
+ *outbytes=d-outbuf;
+ if(u->parsecount<0)
+ u->parsecount=0;
+ else
+ u->parsecount--;
+ }
+ return(simplepacket);
+}
+
+void appendpacket(uchar *buf,int *bytes,uchar *adata,int *abytes)
+{
+ if(*bytes+*abytes>APPENDSIZE) return;
+ memcpy(buf+*bytes,adata,*abytes);
+ *bytes+=*abytes;
+ *abytes=0;
+}
+
+/********************************/
+
+typedef int (*qsortfunc)(const void *a,const void *b);
+
+int cmpnum(const uchar **txt1,const uchar **txt2)
+{
+ const uchar *a,*b;
+ uchar at[3],bt[3];
+ int ai,bi;
+
+ a=*txt1;
+ b=*txt2;
+
+ a+=strlen(a)-3;
+ b+=strlen(b)-3;
+
+ at[0]=a[0]&0x7f;
+ at[1]=a[1]&0x7f;
+ at[2]=0;
+
+ bt[0]=b[0]&0x7f;
+ bt[1]=b[1]&0x7f;
+ bt[2]=0;
+
+ if(at[0]=='?') ai=999;
+ else if(at[1]=='m') ai=100+at[0];
+ else ai=atoi(at);
+
+ if(bt[0]=='?') bi=999;
+ else if(bt[1]=='m') bi=100+bt[0];
+ else bi=atoi(bt);
+
+ return(ai-bi);
+}
+
+void mixoverlays(char *d,char *s1,char *s2)
+{
+ char *dend=d+1000,*d0=d,*s0;
+ int lines=0;
+// logx(LOG_CONSOLE,"mix<%s><%s>\n",s1,s2);
+ while((*s1 || *s2) && d<dend)
+ {
+ while(*s1=='\r') s1++;
+ while(*s2=='\r') s2++;
+
+ if( ((*s1) && (*s1)!='\n') )
+ { // line from s1
+ s0=s1;
+ }
+ else
+ {
+ s0=s2;
+ }
+
+ while(*s0 && *s0!='\n') *d++=*s0++;
+ while(*s1 && *s1!='\n') s1++;
+ while(*s2 && *s2!='\n') s2++;
+ if(*s1 || *s2) *d++='\n';
+ if(*s1=='\n') s1++;
+#if defined(__APPLE__) || defined(MACOSX)
+ // BUG?
+ if(*s2=='\n') s2++;
+#else
+ if(*s2=='\n') *s2++;
+#endif /* __APPLE__ ||ÊMACOSX */
+ lines++;
+ }
+ if(lines>500) *d0=0;
+ *d++=0;
+// logx(LOG_CONSOLE,"mixresult<%s>\n",d0);
+}
+
+void readheader(uchar *inbuf,int *seqthis,int *seqrecv,int *bitthis,int *bitrecv)
+{
+ *seqthis=intat(inbuf+0);
+ *seqrecv=intat(inbuf+4);
+ if(*seqthis==-1) *bitthis=0x80;
+ else
+ {
+ *bitthis=(*seqthis&0x80000000)?0x80:0x00;
+ *seqthis&=0x7fffffff;
+ }
+ if(*seqrecv==-1) *bitrecv=0x80;
+ else
+ {
+ *bitrecv=(*seqrecv&0x80000000)?0x80:0x00;
+ *seqrecv&=0x7fffffff;
+ }
+}
+
+void writeheader(uchar *inbuf,int *seqthis,int *seqrecv,int *bitthis,int *bitrecv)
+{
+ int s1,s2;
+ if(*seqthis==-1) s1=-1;
+ else
+ {
+ s1=*seqthis&0x7fffffff;
+ if(*bitthis) s1|=0x80000000;
+ }
+ if(*seqrecv==-1) s2=-1;
+ else
+ {
+ s2=*seqrecv&0x7fffffff;
+ if(*bitrecv) s2|=0x80000000;
+ }
+ setintat(inbuf+0,s1);
+ setintat(inbuf+4,s2);
+}
+
+int packetmatch(User *u,uchar *buffer)
+{ // 0=exact match, smaller better
+ int nthis,nrecv,value,dummy;
+ int timedelay,ident;
+
+ timedelay=500;
+
+ readheader(buffer,&nthis,&nrecv,&dummy,&dummy);
+ ident=shortat(buffer+8);
+
+ if(u->time<u->time_lastclient+timedelay) return(0x7fffffff);
+ if(nthis<u->client_lastthis) return(0x7fffffff);
+ if(nrecv>u->server_lastthis) return(0x7fffffff);
+ if(u->clientident!=-1 && ident!=u->clientident) return(0x7fffffff);
+ value =abs(nrecv-u->server_lastthis);
+ value+=abs(nthis-u->client_lastthis);
+ return(value);
+}
+
+void q_serverextras(User *u)
+{
+ int i;
+
+ if(u->time>u->time_lastoverlay+1100)
+ {
+ *u->overlay=0;
+ }
+
+ if(u->trackloc)
+ {
+ int i,n;
+ // update one visible player / packet
+ for(i=0;i<32;i++)
+ {
+ u->tracklocind++;
+ if(u->tracklocind>32) u->tracklocind=0;
+ n=u->tracklocind;
+ if(u->playeractive[n])
+ {
+ int l;
+ int old1,old2;
+ findnear(u,u->playerpos[n][0],u->playerpos[n][1],u->playerpos[n][2],&l);
+
+ old1=u->playerlastloc[n];
+ old2=u->playerloc[n];
+
+ u->playerlastloc[n]=u->playerloc[n];
+ u->playerloc[n]=l;
+
+ if(!playerlocdirection(u,n))
+ {
+ u->playerlastloc[n]=old1;
+ u->playerloc[n]=old2;
+ }
+ break;
+ }
+ else
+ {
+ u->playerloc[n]=u->playerlastloc[n]=0;
+ }
+ }
+ }
+
+ if(u->time_reskinall && u->time>u->time_reskinall)
+ {
+ if(u->skinoverride || u->coloroverride)
+ {
+ int limit=2; // 2 updates per packet
+
+ logx(LOG_DEBUG,"reskinall (pos=%i)\n",u->skinoverridepos);
+
+ // reset change counter to start
+ if(u->skinoverridepos>=34)
+ {
+ int i;
+ u->playerfragsbest=0;
+ for(i=0;i<32;i++)
+ {
+ if(u->playerfrags[i]>u->playerfragsbest)
+ {
+ u->playerfragsbest=u->playerfrags[i];
+ }
+ }
+ u->skinoverridepos=0;
+ }
+
+ // change limit skins
+ while(limit>0)
+ {
+ if(u->playeractive[u->skinoverridepos])
+ {
+ limit--;
+ u->time_reskinall=0;
+ if(u->color==-3 || u->color2==-3)
+ {
+ rankchange(u,u->skinoverridepos,0);
+ }
+ skinoverride(u,u->skinoverridepos);
+ u->time_reskinall=u->time;
+ }
+
+ u->skinoverridepos++;
+ if(u->skinoverridepos>=34)
+ {
+ if(u->skinoverride<0) u->skinoverride=0;
+ q_stufftext(u,"skins\n");
+ u->time_reskinall=0;
+ break;
+ }
+ }
+ }
+ else u->time_reskinall=0;
+ }
+ else
+ {
+ if(u->coloroverride && u->color==-3 || u->color2==-3)
+ {
+ // increment counter (all players updated every 32 packets)
+ u->rankoverridepos+=7;
+ if(u->rankoverridepos>=33) u->rankoverridepos-=32;
+ if(u->rankoverridepos>=33) u->rankoverridepos=1;
+
+ if(u->playeractive[u->rankoverridepos])
+ {
+ // rankupdate that player
+ rankchange(u,u->rankoverridepos,1);
+ }
+ }
+ }
+
+ // player nearness check
+ for(i=0;i<u->numplayers;i++)
+ {
+ u->playernear[i]--;
+ if(u->playernear[i]<0) u->playernear[i]=0;
+ }
+
+ if(!u->amspectator && u->time_sendfreport && u->time>u->time_sendfreport)
+ {
+ u->time_sendfreport=0;
+ u->time_lastfreport=u->time;
+ q_stufftext(u,"f_report\n");
+ }
+
+#if 0
+ if(u->pointshow && u->time>u->time_nexttarget)
+ {
+ uchar *n;
+
+ n=findpoint(u,u->pos[0],u->pos[1],u->pos[2],u->ang[0],u->ang[1],u->ang[2],u->pointshow,0,NULL);
+ strcpy(u->msgline,n);
+ strxor80(u->msgline);
+
+ u->time_nexttarget=u->time+100; // 0.1 sec
+ if(strcmp(n,u->lasttarget)) u->time_nextcamp=0;
+ strcpy(u->lasttarget,n);
+ }
+#endif
+
+ if(u->showhud)
+ {
+ static uchar buf[MAXSTR];
+ static uchar buf2[MAXSTR];
+
+ u->overlayactive=1;
+
+ if(u->time>u->time_nextcamp || u->time<u->time_nextcamp-1000)
+ {
+ int a;
+
+ u->time_nextcamp=u->time+100;
+
+ memset(buf,'\n',u->moveoverlay);
+ buf[u->moveoverlay]=0;
+
+ strcat(buf,"\n\n\n\n");
+
+
+ {
+ if(u->items&ITEMS_RA) buf2[0]=0x80|'r';
+ else if(u->items&ITEMS_YA) buf2[0]=0x80|'y';
+ else if(u->items&ITEMS_GA) buf2[0]=0x80|'g';
+ else buf2[0]=0x80|'a';
+ buf2[1]=0x9d;
+ buf2[2]=0x9e;
+ buf2[3]=0x9e;
+ buf2[4]=0x9e;
+ buf2[5]=0x9e;
+ buf2[6]=0x9f;
+ buf2[7]='\n';
+ buf2[8]=0;
+
+ a=6*u->armor/195;
+ if(a<1) a=1;
+ if(a>6) a=6;
+ if(u->armor<50) buf2[a]=0x87;
+ else if(u->armor<101) buf2[a]=0x88;
+ else buf2[a]=0x8d;
+
+ strcat(buf,buf2);
+ }
+
+ {
+ if(u->health>100) buf2[0]=0x80|'m';
+ else buf2[0]=0x80|'h';
+ buf2[1]=0x9d;
+ buf2[2]=0x9e;
+ buf2[3]=0x9e;
+ buf2[4]=0x9e;
+ buf2[5]=0x9e;
+ buf2[6]=0x9f;
+ buf2[7]='\n';
+ buf2[8]=0;
+
+ a=6*u->health/95;
+ if(a<1) a=1;
+ if(a>6) a=6;
+ if(u->health<50) buf2[a]=0x87;
+ else if(u->health<75) buf2[a]=0x88;
+ else buf2[a]=0x8d;
+
+ strcat(buf,buf2);
+ }
+
+ if(u->ammo_rockets<5 && (u->items&ITEMS_RL))
+ {
+ sprintf(buf2,"ammo:%i\n",u->ammo_rockets);
+ strcat(buf,buf2);
+ }
+
+ q_centerprint(u,buf);
+ return;
+ }
+ }
+
+ if(u->time>u->time_nextcamp)
+ {
+ u->time_nextcamp=u->time+1000;
+
+ // determine last active player
+ for(i=33;i>1;i--) if(u->playeractive[i]) break;
+ u->numplayers=i;
+
+ // megahealth check
+ for(i=0;i<u->camps;i++)
+ {
+ Camp *ca;
+ ca=&u->camp[i];
+ if(ca->category&CAMP_MH)
+ {
+ if(u->health<=100 && ca->appeartime==UNKNOWNTIME)
+ {
+ ca->appeartime=20500+u->time;
+ ca->secondcamp=0;
+ }
+ }
+ }
+
+ if(u->showspeed || u->showtime || u->showdist)
+ {
+ *u->msgline=0;
+ u->time_nextcamp=u->time+200;
+ if(u->showtime)
+ {
+ sprintf(u->msgline+strlen(u->msgline),"time:%03i ",((u->time-u->showtimebase)/1000)%1000);
+ }
+ if(u->showspeed)
+ {
+ int s,i;
+ s=0;
+ for(i=0;i<4;i++) s+=u->speed[i];
+ s=s/300;
+ sprintf(u->msgline+strlen(u->msgline),"speed:%03i ",s);
+ }
+ if(u->showdist)
+ {
+ float d,f;
+ f=(float)(u->showdistbase[0]-u->pos[0]);
+ d=f*f;
+ f=(float)(u->showdistbase[1]-u->pos[1]);
+ d+=f*f;
+ f=(float)(u->showdistbase[2]-u->pos[2]);
+ d=(float)sqrt(d);
+
+ sprintf(u->msgline+strlen(u->msgline),"dist:%04i (x:%04i y:%04i z:%04i) ",
+ (int)(d/10),
+ (u->showdistbase[0]-u->pos[0])/10,
+ (u->showdistbase[1]-u->pos[1])/10,
+ (u->showdistbase[2]-u->pos[2])/10);
+ }
+ }
+
+ if(u->campthese||u->specmode||*u->msgline||*u->overlay||*u->notice)
+ { // show to self
+ static uchar buf[MAXSTR];
+ static uchar buf1[MAXSTR]; // first camp line
+ static uchar buf2[MAXSTR]; // second camp line
+#ifdef TIMERS
+ static uchar reports[64][16];
+#endif /* TIMERS */
+ int i;
+
+ memset(buf,0,MAXSTR);
+ memset(buf,'\n',u->linesdown/2+u->moveoverlay);
+ if(*u->notice)
+ {
+ strcat(buf,u->notice);
+ *u->notice=0;
+ }
+ memset(buf+strlen(buf),'\n',u->linesdown-u->linesdown/2-u->moveoverlay);
+
+ // addc lock
+ if(u->specmode)
+ {
+ if(u->specentity==u->entity)
+ {
+#if defined(__APPLE__) || defined(MACOSX)
+ buf[strlen(buf)] = 0x00;
+#else
+ sprintf(buf+strlen(buf),"");
+#endif /* __APPLE__ || MACOSX */
+ }
+ else if(u->specentity==0)
+ {
+ sprintf(buf+strlen(buf),"nobody to track\n");
+ }
+ else
+ {
+ uchar *armor=" ";
+ if(u->items&0x2000)
+ {
+ armor="g";
+ }
+ if(u->items&0x4000)
+ {
+ armor="y";
+ }
+ if(u->items&0x8000)
+ {
+ armor="r";
+ }
+ sprintf(buf+strlen(buf),"%s \x9c \xE8\xE5\xE1\xEC\xF4\xE8 %3i \x9c \xe1\xf2\xed\xef\xf2 %s %3i\n",
+ u->playername[u->specentity],
+ u->health,
+ armor,
+ u->armor);
+ }
+ }
+ else
+ {
+ sprintf(buf+strlen(buf),"%s\n",u->msgline);
+ *u->msgline=0;
+ }
+
+#ifdef TIMERS
+ // generate list of camps
+ if(u->campthese && !(u->fpd&FPD_TIMERS))
+ {
+ uchar *rep[64];
+ int r;
+ r=0;
+ for(i=0;i<u->camps;i++)
+ {
+ Camp *ca;
+ int dtime;
+ char middlechar;
+ ca=&u->camp[i];
+ if((ca->category&u->campthese) && ca->appeartime)
+ {
+ int style=0,tim;
+ dtime=(int)ca->appeartime-(int)u->time;
+
+ if(u->remindcamp && (ca->category&(CAMP_R|CAMP_P|CAMP_Q)) && dtime>=u->remindcamp*1000-1000 && dtime<u->remindcamp*1000+1000 )
+ {
+ sprintf(u->notice,"\x10%s\x11",ca->name);
+ strxor80(u->notice);
+ }
+
+ if(ca->appeartime==UNKNOWNTIME || ca->appeartime==UNKNOWNTIME2)
+ {
+ style=0;
+ }
+ else if(dtime<-3000)
+ {
+ if(u->secondcamp && ca->secondcamp==0 && (ca->category&(CAMP_R|CAMP_P|CAMP_Q)))
+ {
+ ca->appeartime+=ca->delay;
+ dtime+=ca->delay;
+ ca->secondcamp++;
+ style=1;
+ }
+ else
+ {
+ ca->appeartime=0;
+ continue; // don't display
+ }
+ }
+ else
+ {
+ style=1;
+ }
+
+ if(ca->secondcamp) middlechar=0x0d;
+ else middlechar=0x1c;
+
+ tim=(dtime-123)/1000;
+ if(tim<0) tim=0;
+
+ if(!style)
+ {
+ sprintf(reports[r++],"%s%c?? ",ca->name,middlechar);
+ }
+ else
+ {
+ if(tim>99)
+ {
+ tim=(tim+30)/60;
+ sprintf(reports[r++],"%s%c%cm ",ca->name,middlechar,tim%10+'0');
+ }
+ else
+ {
+ if(tim<10) sprintf(reports[r++],"%s%c%c%c ",ca->name,middlechar,tim/10+'0'+0x80,tim%10+'0'+0x80);
+ else sprintf(reports[r++],"%s%c%c%c ",ca->name,middlechar,tim/10+'0',tim%10+'0');
+ }
+ }
+ }
+ }
+
+ // sort list of camps
+ if(r)
+ {
+ for(i=0;i<r;i++) rep[i]=reports[i];
+ qsort(rep,r,sizeof(uchar *),(qsortfunc)cmpnum);
+ }
+
+ // add camps to two camp rows
+ buf1[0]=' '; buf1[1]=0;
+ buf2[0]=' '; buf2[1]=0;
+ {
+ int r1=0,r2=0;
+ for(i=0;i<r;i++)
+ {
+ if(rep[i][1]>='0' && rep[i][1]<='9' &&
+ (rep[i][0]=='p' || rep[i][0]=='r' || rep[i][0]=='q'))
+ {
+ if(r1<5)
+ {
+ strcat(buf1,rep[i]);
+ r1++;
+ }
+ }
+ }
+ for(i=0;i<r;i++)
+ {
+ if(rep[i][1]>='0' && rep[i][1]<='9' &&
+ (rep[i][0]=='p' || rep[i][0]=='r' || rep[i][0]=='q'))
+ {
+ }
+ else
+ {
+ if(r1+r2<5)
+ {
+ strcat(buf2,rep[i]);
+ r2++;
+ }
+ }
+ }
+ }
+ }
+ else
+#endif
+ {
+ *buf2=0;
+ *buf1=0;
+ }
+
+ {
+ uchar *d,*s;
+ int l;
+
+ d=buf+strlen(buf);
+
+ s=buf1;
+ while(*s)
+ {
+ *d++=(*s++)^0x80;
+ }
+
+ if(!u->linecenter)
+ {
+ l=u->linesright-strlen(buf1)-strlen(buf2);
+ if(l<0) l=0;
+ memset(d,' ',l); d+=l;
+ }
+
+ s=buf2;
+ while(*s)
+ {
+ *d++=(*s++)^0x80;
+ }
+ }
+
+ // check emptiness, print if not empty
+ for(i=strlen(buf)-1;i>=0;i--)
+ {
+ if((buf[i]&127)>32) break;
+ }
+ if(i<=0)
+ {
+ buf[0]=0;
+ u->overlayactive=0;
+ }
+ else u->overlayactive=1;
+
+ if(*u->overlay)
+ {
+ mixoverlays(tmpbuf,buf,u->overlay);
+ if(*tmpbuf) q_centerprint(u,tmpbuf); // something to show
+ }
+ else
+ {
+ if(*buf) q_centerprint(u,buf); // something to show
+ }
+ }
+ else u->overlayactive=0;
+ }
+}
+
+void q_server(User *u,uchar *inbuf,int inbytes)
+{
+ int seqthis,seqrecv;
+ int bitthis,bitrecv;
+ int outbytes;
+
+ if(u->amconnected>0) u->amconnected--;
+// if((rand()&15)<9) return; // throw away packet
+
+ u->packetserveroutsize[u->packetserversizecnt]=inbytes;
+#ifdef COMPRESS
+ uncompress(u,inbuf,&inbytes,1);
+#endif
+ u->packetserverinsize[u->packetserversizecnt]=inbytes;
+
+ readheader(inbuf,&seqthis,&seqrecv,&bitthis,&bitrecv);
+
+ if(seqthis!=-1)
+ {
+ if(seqthis<u->server_lastthis-30)
+ {
+ u->outoforder30++;
+ }
+ if(seqthis<u->server_lastthis)
+ {
+ u->outoforder++;
+ }
+ if(seqthis<u->connectstartsequence)
+ {
+ logx(LOG_DEBUG,"packet out of sequence, dropped.\n");
+ return;
+ }
+ u->server_hiddenbit=bitthis;
+ u->server_lastthis=seqthis;
+ u->server_lastrecv=seqrecv;
+ }
+
+ if(u->packetserversizecnt==0)
+ { // every 16 packets, calc ping
+ int n,i,ping=0,pingcnt=0;
+ for(n=0;n<16;n++)
+ {
+ i=(STATNUM+u->lastpacketrecv-1-n)&(STATNUM-1);
+ if(u->packetrecvtime[i])
+ {
+ ping+=u->packetrecvtime[i]-u->packetsendtime[i];
+ pingcnt++;
+ }
+ }
+ if(!pingcnt) ping=999;
+ else ping=ping/pingcnt+1; // raw round up
+ if(ping<=0) ping=1;
+ u->server_ping=ping;
+ }
+
+ if(loglevel>=LOG_DEBUG) logx(LOG_DEBUG,"receive %02X %02X time %i\n",inbuf[0],inbuf[4],u->time);
+
+ handleserver(u,inbuf,inbytes,outbuf,&outbytes);
+
+ if(u->cnt_model>1 || u->cnt_sound>1)
+ {
+ q_print(u,"Error connecting to server.\n");
+ outbytes=8; // remove all but header
+ }
+ expires_check();
+
+ q_serverextras(u);
+
+ if(!outbytes) return;
+
+ if(u->noappendnow>0) u->noappendnow--;
+ else
+ {
+ if(u->appendserverbytes)
+ {
+ appendpacket(outbuf,&outbytes,u->appendserver,&u->appendserverbytes);
+ logx(LOG_PACKETS,"Packet APPENDED!\n");
+ u->dumpsentpacket=1;
+ }
+ }
+
+ writeheader(outbuf,&seqthis,&seqrecv,&bitthis,&bitrecv);
+
+ if(u->compress_test)
+ {
+#ifdef COMPRESS
+ static char tmp[4096];
+ int tmpbytes;
+
+ u->compress_server=1;
+
+ tmpbytes=outbytes;
+ memcpy(tmp,outbuf,tmpbytes);
+
+ compress(u,tmp,&tmpbytes,1);
+ c_hexdump("CTEST server compressed:",tmp,tmpbytes,u);
+
+ u->packetserveroutsize[u->packetserversizecnt]=tmpbytes;
+
+ uncompress(u,tmp,&tmpbytes,1);
+ c_hexdump("CTEST server uncompressed:",tmp,tmpbytes,u);
+
+ u->packetserverinsize[u->packetserversizecnt]=tmpbytes;
+
+ u->compress_server=0;
+
+ if(outbytes!=tmpbytes)
+ {
+ logx(LOG_PACKETS,"\nCTEST COMPRESS MISMATCH!\n");
+ }
+ logx(LOG_PACKETS,"\n");
+
+ if(u->compress_test>1)
+ {
+ outbytes=tmpbytes;
+ memcpy(outbuf,tmp,outbytes);
+ }
+#endif
+ }
+ else
+ {
+#ifdef COMPRESS
+ compress(u,outbuf,&outbytes,1);
+#endif
+ }
+
+ if(seqrecv!=-1)
+ {
+ int i;
+ u->packetrecvtime[seqrecv&(STATNUM-1)]=u->time;
+ u->lastpacketrecv=seqrecv&(STATNUM-1);
+ u->packetchoke [seqrecv&(STATNUM-1)]=u->choke;
+ for(i=0;i<u->choke;i++)
+ {
+ u->packetrecvtime[(seqrecv-1-i)&(STATNUM-1)]=u->time;
+ u->packetchoke [(seqrecv-1-i)&(STATNUM-1)]=-1;
+ }
+
+ u->packetserversize[u->packetserversizecnt]=inbytes;
+ u->packetserversizecnt=(u->packetserversizecnt+1)&15;
+ }
+
+ c_sendclient(u,outbuf,outbytes);
+
+ if(u->cnt_model>1 || u->cnt_sound>1)
+ {
+ u->cnt_model=u->cnt_sound=0;
+ q_command(u,"!disconnect");
+ }
+}
+
+void q_client(User *u,uchar *inbuf,int inbytes)
+{
+ int seqthis,seqrecv;
+ int bitthis,bitrecv;
+ int outbytes,simple;
+#ifdef COMPRESS
+ int inbytes0=inbytes;
+#endif /* COMPRESS */
+
+// if((rand()&15)<9) return; // throw away packet
+
+#ifdef COMPRESS
+ uncompress(u,inbuf,&inbytes,0);
+#endif
+
+ readheader(inbuf,&seqthis,&seqrecv,&bitthis,&bitrecv);
+
+ if(u->cpps)
+ {
+ if(u->time<u->time_nextcpps && !bitthis)
+ { // drop packet
+ u->cppsdrop++;
+ return;
+ }
+ u->cppspass++;
+ // accept packet, calc ok arrival for next packet
+ u->time_nextcpps+=u->time_cppsdelta;
+ if(u->time_nextcpps<u->time)
+ {
+ u->time_nextcpps=u->time+u->time_cppsdelta;
+ }
+ }
+
+ if(seqthis!=-1)
+ {
+ u->sequence=seqthis+1;
+ u->client_lastthis=seqthis;
+ u->client_lastrecv=seqrecv;
+
+ u->packetsendtime[seqthis&(STATNUM-1)]=u->time;
+ u->packetrecvtime[seqthis&(STATNUM-1)]=0;
+ u->packetchoke [seqthis&(STATNUM-1)]=0;
+
+ u->packetclientsize[u->packetclientsizecnt]=inbytes;
+ u->packetclientsizecnt=(u->packetclientsizecnt+1)&15;
+ }
+
+#ifdef IDENT
+ if(u->clearrequest)
+ {
+ sendinfomessage(u,"\"\"");
+ u->clearrequest=0;
+ }
+#endif
+
+ simple=handleclient(u,inbuf,inbytes,outbuf,&outbytes);
+ if(u->appendclientbytes>0) simple=0;
+ // simple=only movement & sequence in packet
+
+ if(u->noappendnow>0) u->noappendnow--;
+ else
+ {
+ if(u->appendclientbytes)
+ {
+ appendpacket(outbuf,&outbytes,u->appendclient,&u->appendclientbytes);
+ logx(LOG_PACKETS,"Packet APPENDED!\n");
+ u->dumpsentpacket=1;
+ }
+ }
+
+ if(u->fakedisablecnt>0) u->fakedisablecnt--;
+ else if(u->fakeping)
+ {
+ u->fakedisablecnt=0;
+ if(u->fakeping<=-50)
+ {
+ seqrecv=u->server_lastthis-(u->fakeping+100);
+ }
+ else
+ {
+ seqrecv-=u->fakeping;
+ }
+ }
+
+ writeheader(outbuf,&seqthis,&seqrecv,&bitthis,&bitrecv);
+
+ if(u->compress_test)
+ {
+#ifdef COMPRESS
+ static char tmp[4096];
+ int tmpbytes;
+
+ u->compress_client=1;
+
+ tmpbytes=outbytes;
+ memcpy(tmp,outbuf,tmpbytes);
+
+ compress(u,tmp,&tmpbytes,0);
+ c_hexdump("CTEST client compressed:",tmp,tmpbytes,u);
+
+ u->packetclientoutsize[u->packetclientsizecnt]=tmpbytes;
+
+ uncompress(u,tmp,&tmpbytes,0);
+ c_hexdump("CTEST client uncompressed:",tmp,tmpbytes,u);
+
+ u->packetclientinsize[u->packetclientsizecnt]=tmpbytes;
+
+ u->compress_client=0;
+
+ if(outbytes!=tmpbytes)
+ {
+ logx(LOG_PACKETS,"\nCTEST COMPRESS MISMATCH!\n");
+ }
+ logx(LOG_PACKETS,"\n");
+
+ if(u->compress_test>1)
+ {
+ outbytes=tmpbytes;
+ memcpy(outbuf,tmp,outbytes);
+ }
+#endif
+ }
+ else
+ {
+ u->packetclientinsize[u->packetclientsizecnt]=outbytes;
+#ifdef COMPRESS
+ compress(u,outbuf,&outbytes,0);
+#endif
+ u->packetclientoutsize[u->packetclientsizecnt]=outbytes;
+ }
+
+ c_sendserver(u,outbuf,outbytes);
+
+ logx(LOG_DEBUG,"send %02X %02X time %i\n",outbuf[0],outbuf[4],u->time);
+
+ if(u->state==STATE_DROPPING)
+ {
+ c_sendserver(u,outbuf,outbytes);
+ c_sendserver(u,outbuf,outbytes);
+ // send thanks
+ outbytes=8;
+ seqthis=u->sequence+1;
+ bitthis=0;
+ writeheader(outbuf,&seqthis,&seqrecv,&bitthis,&bitrecv);
+ if(u->noappendnow>0) u->noappendnow--;
+ else
+ {
+ if(u->appendserverbytes)
+ {
+ appendpacket(outbuf,&outbytes,u->appendserver,&u->appendserverbytes);
+ logx(LOG_PACKETS,"Packet APPENDED!\n");
+ u->dumpsentpacket=1;
+ }
+ }
+ c_sendclient(u,outbuf,outbytes);
+ }
+}
+
+void q_proxy(User *u,uchar *inbuf,int inbytes)
+{
+ int seqthis,seqrecv;
+ int bitthis,bitrecv;
+ int outbytes;
+
+ // handle input client packet
+
+ u->amconnected=0;
+ u->server_ping=0;
+
+ uncompress(u,inbuf,&inbytes,0);
+
+ readheader(inbuf,&seqthis,&seqrecv,&bitthis,&bitrecv);
+
+ if(seqthis!=-1)
+ {
+ u->sequence=seqthis+1;
+
+ u->client_lastthis=seqthis;
+ u->client_lastrecv=seqrecv;
+
+ if(bitthis) u->server_hiddenbit^=0x80;
+ u->client_hiddenbit=bitrecv;
+ }
+
+ if(u->state==STATE_NAMELOOKUP && u->time>=u->time_namelookupmsg)
+ {
+ q_print(u,"performing namelookup...\n");
+ u->time_namelookupmsg=u->time+2000;
+ }
+
+ handleclient(u,inbuf,inbytes,outbuf,&outbytes);
+
+ // create output server packet
+
+ outbytes=8;
+ appendpacket(outbuf,&outbytes,u->appendserver,&u->appendserverbytes);
+
+ seqthis=u->sequence;
+ bitrecv=u->server_hiddenbit;
+ bitthis=0;
+ if(u->server_sendsecure)
+ {
+ bitthis=0x80;
+ u->server_sendsecure=0;
+ }
+
+ if(seqthis!=-1)
+ {
+ u->server_lastthis=seqthis;
+ u->server_lastrecv=seqrecv;
+ }
+
+ writeheader(outbuf,&seqthis,&seqrecv,&bitthis,&bitrecv);
+
+ compress(u,outbuf,&outbytes,1);
+
+ u->dumpsentpacket=1;
+ c_sendclient(u,outbuf,outbytes);
+}
+
+void q_connect(uchar *inbuf,int inbytes,SOCKADDR_IN *from)
+{
+ uchar buf[MAXSTR];
+ User *u;
+ if(!memicmp(inbuf+4,"getchal",7) )
+ { // challenge to proxy
+ u=c_finduser(from);
+ if(!u)
+ { // new user
+ u=c_newuser(from);
+ if(!u)
+ { // server full
+ c_textaddress(buf,from);
+ logx(LOG_CONSOLE,"Proxy full (connect attempt from %s)\n",buf);
+ // send connect reply
+ buf[0]=255;
+ buf[1]=255;
+ buf[2]=255;
+ buf[3]=255;
+ strcpy(buf+4,"nproxy is full\n");
+ c_sendto(from,buf,strlen(buf));
+ return;
+ }
+ else
+ {
+ strcpy(u->name,"<challenge>");
+ c_textaddress(buf,from);
+ logx(LOG_VERBOSE,"Challenge: from %s (total %i users)\n",buf,usernum);
+ }
+ }
+ else
+ {
+ //c_textaddress(buf,from);
+ //logx(LOG_VERBOSE,"Dup connect ignored: %s from %s\n",u->name,buf);
+ return;
+ }
+ challenge+=7;
+ // send connect reply
+ buf[0]=255;
+ buf[1]=255;
+ buf[2]=255;
+ buf[3]=255;
+ sprintf(buf+4,"c%i",challenge);
+ c_sendclient(u,buf,strlen(buf));
+ u->state=STATE_DROPPING; // this connection no inter needed
+ }
+ else if(!memicmp(inbuf+4,"status",6) || !memicmp(inbuf+4,"ping",4) || inbuf[4]=='k')
+ { // status / ping
+ int i,isping=memicmp(inbuf+4,"status",6);
+ u=c_finduser(from);
+ if(!u)
+ { // new user
+ u=c_newuser(from);
+ if(!u)
+ {
+ return;
+ }
+ else
+ {
+ strcpy(u->name,"<status>");
+ c_textaddress(buf,from);
+ logx(LOG_VERBOSE,"Status: from %s\n",buf);
+ }
+ }
+ // generate hostname
+ if(!*proxy_name)
+ {
+ gethostname(proxy_name,255);
+ strlwr(proxy_name);
+ }
+ // send connect reply
+ buf[0]=255;
+ buf[1]=255;
+ buf[2]=255;
+ buf[3]=255;
+ if(isping)
+ {
+ c_sendclient(u,buf,4);
+ }
+ else
+ {
+ int min,ping;
+ sprintf(buf+4,"n\\support\\QW 2.1,2.2,2.3\\info\\QuakeWorld proxy\\maxclients\\%i\\*version\\%s\\hostname\\%s - %s\n",
+ maxusernum,version,versionname,proxy_name);
+ for(i=0;i<usernum;i++) if(u!=user+i)
+ {
+ ping=user[i].server_ping;
+ ping=0;
+ min=(user[i].time-user[i].time_connect)/1000/60;
+ c_textaddress(tmpbuf,&user[i].servermainaddr);
+ if(user[i].state==STATE_HERE) strcpy(tmpbuf,"idle");
+ sprintf(buf+strlen(buf),"0 0 %i %i \"%s\" \"%s\" %i %i\n",
+ min,ping,user[i].name,tmpbuf,user[i].tcolor,user[i].bcolor);
+ }
+ c_sendclient(u,buf,strlen(buf)+1);
+ }
+ u->state=STATE_DROPPING; // this connection no inter needed
+ }
+ else if(!memicmp(inbuf+4,"connect ",8))
+ { // connect to proxy
+ c_textaddress(buf,from);
+ u=c_finduser(from);
+
+ if(!u)
+ { // new user
+ if(usernum>=maxusernum) u=NULL;
+ else u=c_newuser(from);
+ if(!u)
+ { // server full
+ logx(LOG_CONSOLE,"Proxy full (connect attempt from %s)\n",buf);
+ // send connect reply
+ buf[0]=255;
+ buf[1]=255;
+ buf[2]=255;
+ buf[3]=255;
+ strcpy(buf+4,"nproxy is full\n");
+ c_sendto(from,buf,strlen(buf));
+ u->state=STATE_DROPPING; // this connection no inter needed
+ return;
+ }
+ else
+ {
+ int deny=0;
+ uchar *p;
+ p=inbuf+4+8;
+ if(*p=='\"')
+ {
+ u->version=21;
+ strcpy(u->clientinfo,p);
+ }
+ else
+ {
+ while(*p<=32 && *p) p++;
+ u->response_a=atoi(p);
+ while(*p>32) p++;
+ while(*p<=32 && *p) p++;
+ u->response_b=atoi(p);
+ while(*p>32) p++;
+ while(*p<=32 && *p) p++;
+ u->response_c=atoi(p);
+ while(*p>32) p++;
+ while(*p<=32 && *p) p++;
+
+ if(u->response_a<=26) u->version=22;
+ else if(u->response_a<=27) u->version=23;
+ else u->version=24;
+
+ strcpy(u->clientinfo,p);
+ }
+
+ extractfield(tmpbuf,u->clientinfo,"topcolor\\");
+ u->tcolor=atoi(tmpbuf);
+ extractfield(tmpbuf,u->clientinfo,"bottomcolor\\");
+ u->bcolor=atoi(tmpbuf);
+ extractfield(u->name,u->clientinfo,"name\\");
+
+ extractfield(tmpbuf,u->clientinfo,"password\\");
+
+ if(*proxy_password && strcmp(tmpbuf,proxy_password))
+ {
+ logx(LOG_CONSOLE,"Connection not allowed (wrong password from %s)\n",buf);
+ // send connect reply
+ buf[0]=255;
+ buf[1]=255;
+ buf[2]=255;
+ buf[3]=255;
+ strcpy(buf+4,"nincorrect password. connection not allowed.\n");
+ c_sendto(from,buf,strlen(buf));
+ u->state=STATE_DROPPING; // this connection no inter needed
+ return;
+ }
+
+ deny=0;
+ if(*proxy_allowip && memicmp(buf,proxy_allowip,strlen(proxy_allowip))) deny=1;
+ if(proxy_denyipnum)
+ {
+ int i;
+ for(i=0;i<proxy_denyipnum;i++)
+ {
+ if(memicmp(buf,proxy_denyip[i],strlen(proxy_denyip[i])))
+ {
+ deny=1;
+ break;
+ }
+ }
+ }
+ if(deny)
+ {
+ logx(LOG_CONSOLE,"Connection not allowed (connect attempt from %s)\n",buf);
+ // send connect reply
+ buf[0]=255;
+ buf[1]=255;
+ buf[2]=255;
+ buf[3]=255;
+ strcpy(buf+4,"nconnection not allowed from your ip address\n");
+ c_sendto(from,buf,strlen(buf));
+ u->state=STATE_DROPPING; // this connection no inter needed
+ return;
+ }
+
+ u->showtime=0;
+ u->campthese=0;
+// u->reportthese=0;
+// u->pickupthese=0;
+ u->time_nextcamp=0;
+ u->linesdown=11;
+ u->linesright=36;
+ u->time_namelookupmsg=0;
+ u->sequence=0;
+
+ logx(LOG_CONSOLE,"Connect: %s from %s (ver %i, users %i)\n",u->name,buf,u->version,usernum);
+ }
+ }
+ else
+ {
+ //logx(LOG_VERBOSE,"Dup connect ignored: %s from %s\n",u->name,buf);
+ return;
+ }
+ // send connect reply
+ buf[0]=255;
+ buf[1]=255;
+ buf[2]=255;
+ buf[3]=255;
+ buf[4]='j';
+ c_sendclient(u,buf,5);
+ u->state=STATE_INIT;
+ }
+ else
+ {
+ // skip
+ if(loglevel>=LOG_DEBUG)
+ {
+ c_textaddress(buf,from);
+ logx(LOG_DEBUG,"Unrecognized connect attempt from %s.\n",buf);
+ }
+ }
+}
+
--- /dev/null
+// Cheapo - a QuakeWorld Proxy
+// Copyright (C) 1998 Sami Tammilehto
+//
+// 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.
+
+//---------------- configuration -------------------------------
+
+//#define PROMODE
+// enable debug/development features:
+// - .debug command (includes misc stuff and cheat tests)
+// - enables logging levels 3-5 (4+=full protocol log to proxy.log)
+// - timing for all items (not just powerups)
+// note: compiling with promode results in a different f_modified checksum.
+
+//#define RLPACK
+// If you point to a pack left by a player that you saw fired
+// a rocket just before death, the pack is tagged as 'rl-pack'.
+// This proved way too inaccurate (like 1 pack in 20 with luck
+// would be classified rl-pack) for practical use, and was also
+// too cheatish, so it wasn't published or used.
+
+//#define IDENT
+// enable ident of teammates. Pointing a teammate and pressing '.ident'
+// shows stats for that player (f_report) in your hud without usin
+// messages. This feature didn't prove too useful, is complicated,
+// and requires everyone uses Cheapo so it was never published.
+
+//#define POWERDROP
+// allows .rep power2 which reports separately when powerups are
+// picked and when they time out. Decided not to make this public
+// since at the time powerup timers were going away.
+
+//#define COMPRESS
+// enable compression. Also requires PROMODE (.debug com enables)
+// Compress.c only contains compression stubs (compress_raw and
+// uncompress_raw) that don't really compress anything. I didn't
+// have time to develop a compression protocol, so only the
+// framework is in place. I did find out that you need a very
+// specialized algorithm (since the packets are so small and
+// they may be dropped) like the excellent one in Qizmo to get
+// any useful results.
+
+//#define SPEED
+// '.show speed' and '.show time' commands which allow for
+// timing routes and checking movement speed (buggy)
+
+//#define TIMERS
+// enable powerup timers
+
+//#define MAINPROFILE
+// enable some performance profiling
+
+//#define EXPIRETIME 920316146 // 1.3.1998
+// expiration time, used for beta versions
+
+//#define PROTECT
+// enable f_modified, f_version, exe integrity check
+// (code for these is not present in open version)
+
+//#define UNIXALPHA
+// use settings suitable for Alpha unix
+// Windows/unix selection is based on define WIN32 which
+// must be used in windows.
+
+//---------------- other defines based on above ----------------
+
+#ifdef PROMODE
+#define PACKETLOG
+// enable logging of qw protocol in decoded form
+#endif
+
+#ifndef WIN32
+
+#ifdef UNIXALPHA
+#define VERCODE "a"
+#define ALPHA
+#undef PROTECT
+#else
+#define VERCODE "l"
+#endif
+
+// what to use for real time clock
+
+// UNIX settings
+#define GETTIMEOFDAY // linux: use gettimeofday system call
+//#define FTIME // alpha: use ftime system call
+//#define HRTIME // sparc: use sparc high resolution timer
+#define HILO // all: unknown byte ordering
+
+#else
+
+#define VERCODE "w"
+
+// what to use for real time clock
+
+// WINDOWS settings
+//#define FTIME // use ftime system call
+//#define WINTIME // use windows getcurrenttime
+#define PERFTIME // use performance counter
+
+#endif
+
+//----------------------------------------------------------------
+
+#ifdef WIN32
+
+#include <windows.h>
+#include <sys/timeb.h>
+
+#else
+
+/*#include <sys/timeb.h>*/
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <unistd.h>
+
+#define SOCKET int
+#define SOCKET_ERROR -1
+#define SOCKADDR_IN struct sockaddr_in
+#define DWORD unsigned int
+#define INVALID_SOCKET -1
+#define LPSOCKADDR struct sockaddr *
+#define closesocket close
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string.h>
+#include <math.h>
+
+typedef unsigned char uchar;
+
+#define LOG_CONSOLE 1 // messages also shown at console
+#define LOG_VERBOSE 2 // more info
+#define LOG_DEBUG 3 // debug messages
+#define LOG_PACKETS 4 // packet traffic dump
+#define LOG_FLUSH 5 // packet traffic dump with fflush
+#define MAXPLAYERS 16
+#define MAXUSERS (MAXPLAYERS+2)
+
+#define CLIENTINFOSIZE 512
+#define APPENDSIZE 1024 // max data to append to packets
+#define MAXSTR 256
+#define BUFSIZE 16384 // network buffers
+#define MAXPLAYERNAME 32
+#define MAXSKINNAME 32
+#define MAXPROXYNAME 32
+
+#define MAXMODELS 256
+#define MAXSOUNDS 256
+#define MAXENTITIES 512
+#define STATNUM 256 // pinghistory
+#define MAXCAMP 256 // number of objects
+
+#define TIMEOUT 60000 // 60 sec timeout
+#define MAXERRORS 64 // if more than this net errors, autodisconnect
+
+#define SEQUENCES 8 // impulse sequences
+
+#define TOOKRADIUS 900
+#define NEARDIST 6000 // %o,%e
+
+#define CAMP_P 0x0001
+#define CAMP_Q 0x0002
+#define CAMP_R 0x0004
+#define CAMP_LASTPOWER 0x0004
+#define CAMP_MH 0x0008
+#define CAMP_RA 0x0010
+#define CAMP_YA 0x0020
+#define CAMP_GA 0x0040
+#define CAMP_SUIT 0x0080
+#define CAMP_LASTARMOR 0x0080
+#define CAMP_RL 0x0100
+#define CAMP_LG 0x0200
+#define CAMP_GL 0x0400
+#define CAMP_LASTAUTOMARK 0x0400
+#define CAMP_SNG 0x0800
+#define CAMP_NG 0x1000
+#define CAMP_SSG 0x2000
+#define CAMP_RUNE 0x4000 // also key and flag
+#define CAMP_HEALTH 0x8000
+#define CAMP_CELLS 0x10000
+#define CAMP_ROCKETS 0x20000
+#define CAMP_NAILS 0x40000
+#define CAMP_SHELLS 0x80000
+#define CAMP_PLAYER 0x100000
+#define CAMP_GLOW 0x100000 // glowing player
+#define CAMP_PACK 0x200000
+#define CAMP_POINTED 0x400000 // last pointed item
+#define CAMP_ENEMY 0x800000
+#define CAMP_LAST 0x1000000
+
+#define CAMP_DEF (CAMP_PLAYER|CAMP_RA|CAMP_GA|CAMP_YA|CAMP_MH|CAMP_Q|CAMP_R|CAMP_P|CAMP_PACK|CAMP_RL|CAMP_GL|CAMP_LG|CAMP_RUNE|CAMP_SNG|CAMP_ROCKETS)
+#define CAMP_DEFTOOK (CAMP_RA|CAMP_YA|CAMP_RL|CAMP_LG|CAMP_RUNE)
+#define CAMP_POWER (CAMP_R|CAMP_P|CAMP_Q)
+
+// quakeworld 32 bit items word
+#define ITEMS_SG 0x00000001
+#define ITEMS_SSG 0x00000002
+#define ITEMS_NG 0x00000004
+#define ITEMS_SNG 0x00000008
+#define ITEMS_GL 0x00000010
+#define ITEMS_RL 0x00000020
+#define ITEMS_LG 0x00000040
+#define ITEMS_GA 0x00002000
+#define ITEMS_YA 0x00004000
+#define ITEMS_RA 0x00008000
+#define ITEMS_MEGA 0x00010000
+#define ITEMS_SILVER 0x00020000
+#define ITEMS_GOLD 0x00040000
+#define ITEMS_R 0x00080000
+#define ITEMS_P 0x00100000
+#define ITEMS_SUIT 0x00200000
+#define ITEMS_Q 0x00400000
+#define ITEMS_RESIST 0x10000000
+#define ITEMS_STRENGTH 0x20000000
+#define ITEMS_HASTE 0x30000000
+#define ITEMS_REGEN 0x40000000
+#define ITEMS_FLAG (ITEMS_SILVER|ITEMS_GOLD) // flag=keys
+
+typedef struct Camp_t Camp;
+struct Camp_t
+{
+ int entity; // entity number
+ int pos[3];
+ int delay; // re appear delay
+ int category;
+ int secondcamp;
+ DWORD appeartime; // 0=unknown
+ uchar name[4];
+ uchar genericname[8];
+ uchar *pickupsound;
+};
+#define UNKNOWNTIME 0xffffffff
+#define UNKNOWNTIME2 0xfffffffe
+
+typedef struct Location Location;
+struct Location
+{
+ int x,y,z;
+ char *name;
+};
+
+typedef struct User User;
+struct User
+{
+ // communication
+ char proxyhostname[16]; // max 10 chars+port actually
+ SOCKET toserver;
+ //SOCKET toclient;
+ SOCKADDR_IN toserveraddr;
+ SOCKADDR_IN toclientaddr;
+ SOCKADDR_IN servermainaddr; // server 27500 port
+ SOCKADDR_IN lastservermainaddr; // last server 27500 port
+ int safemode; // just forward packets, no parsing
+ int sequence; // last received from client
+ int connectstartsequence; // sequence when connect initiated
+ int server_lastthis;
+ int server_lastrecv;
+ int server_hiddenbit;
+ int server_sendsecure; // command flag
+ int client_lastthis;
+ int client_lastrecv;
+ int client_hiddenbit;
+ int client_sendsecure; // commnd fla
+ int server_ping;
+ int errors; // in net traffic;
+ int version; // 21 or 22
+ int dumpsentpacket;
+ int linkedserver; // linked to a serverside proxy
+ int linkedclient; // linked to a clientside proxy
+ int choke; // for last server packet
+
+ int noappendnow;
+ int quiet;
+
+ int challenge;
+ int havechallenge;
+ int response_a; // version
+ int response_b; // portid
+ int response_c; // challenge
+ int clientident; // 16 bit client ident
+ // append data to packets
+ int appendclientbytes;
+ uchar appendclient[APPENDSIZE];
+ int appendserverbytes;
+ uchar appendserver[APPENDSIZE];
+ // msg buffers for centerprinting
+ uchar msgline[64];
+ uchar notice[64]; // near center of screen
+ // timed events & state
+ DWORD time; // current time in milliseconds (set by main)
+ DWORD time_connect;
+ DWORD time_forcedisconnect;
+ DWORD time_lastserver; // last msg from server (timeout detect)
+ DWORD time_lastclient; // last msg from client (timeout detect)
+ DWORD time_nextcamp; // next camping info on screen
+ DWORD time_nexttarget; // next target check
+ DWORD time_nextreport; // next report
+ DWORD time_reskinall;
+ DWORD time_lastfreport;
+ DWORD time_sendfreport;
+ DWORD time_namelookupmsg;
+ DWORD time_nexttry; // try next connect at this time
+ DWORD time_lastenemymask;
+ DWORD time_lastoverlay;
+ DWORD time_lasttook;
+ DWORD time_lastpoint;
+ DWORD time_nextcpps; // ok to send client packet at this time
+ DWORD time_cppsdelta; // minimum timedelta between packets
+ DWORD time_died;
+ DWORD time_lastfversion;
+ DWORD time_lastsay[4];
+ int state;
+// disconnecting/disconnected from proxy
+#define STATE_DROPPING -1
+// connecting to proxy
+#define STATE_INIT 0
+// connected to proxy
+#define STATE_HERE 1
+// doing namelookup from connecthere=>servermainaddr
+#define STATE_NAMELOOKUP 2
+// trying to connect to servermainaddr
+#define STATE_TRYING 3
+// connected to a server
+#define STATE_CONNECTED 4
+// dropping connection
+#define STATE_DISCONNECTING 5
+ // connecting control variables
+ int doconnect; // when in STATE_HERE, connect to addr below
+ int donew;
+ int isreconnect; // this connect is a reconnect
+ uchar connecthere[MAXSTR]; // ip address for next connect
+ int connectingcount; // try this many times
+ int challengecount; // if 0 say server not responding
+ int dropcount; // how long to wait for server disconnect after drop
+ int firsttry;
+ int servertimeoutmsg; // flag
+ int parsecount;
+ // operation modes
+ uchar proxysay[32]; // "say proxy:"
+ uchar proxysay2[32]; // "say <host>:"
+ int fps_explosion;
+ int fps_powerup;
+ int fps_muzzle;
+ int fps_gib;
+ int fps_rocket;
+ int fps_setinfo; // tbd
+ int fps_nails;
+ int fps_damage;
+ int usepercentn;
+ int campthese;
+ int pointshow;
+ int pointtell;
+ int remindcamp;
+ int secondcamp;
+ int cpps;
+ int linesdown;
+ int linesright;
+ int linecenter;
+ int tellteam;
+ int floodprot;
+ int specmode;
+ int specentity;
+ int bestweapon;
+ int autofreport;
+ int autoihave;
+ int autoitook;
+ int autoitookitems;
+ int autoqpicked;
+ int moveoverlay;
+ int soundtrigger;
+ int classifypacks;
+ int fmodified;
+ int forcedmm; // 0=no force (use server)
+// int reportthese;
+// int reporttime; // report this many seconds before item
+// int pickupthese;
+ char impulseorder[SEQUENCES][16]; // modifiers for impulses (first number is impulse, 0=unused)
+ int notimers;
+
+ // skin settings
+ int skinoverride;
+ int coloroverride;
+ int skinoverridepos; // for sending a few skins per packet
+ int rankoverridepos; // for sending a few skins per packet
+ char skinname[16]; // ours
+ char skinname2[16]; // others
+ int color;
+ int color2;
+
+ // compress settings
+ int compress_client;
+ int compress_server; // set if incoming compressed client messages
+ int compress_test;
+
+ // debug settings
+ int nofversion;
+ int fakeping;
+ int fakedisablecnt;
+ int zztest;
+ int showtime;
+ int showspeed;
+ int showhud;
+ DWORD showtimebase;
+ int showdist;
+ int showdistbase[3];
+ int hideaxemen;
+ int turbo;
+ int fastmove;
+ int shoot;
+ int stoptime;
+
+ // infomessages
+ DWORD time_request;
+ int requestfrom; // 0=nothing now
+ int clearrequest;
+ char requestformat[64];
+
+ // client info
+ int amconnected; // to a server
+ int amspectator;
+ uchar overlay[256]; // server text overlay
+ int overlayactive; // our overlay
+ uchar name[MAXSTR];
+ int team,zero2;
+ int tcolor,bcolor;
+ int lastbegin; // last begin x command
+ uchar clientinfo[CLIENTINFOSIZE];
+ int entity;
+ int health;
+ int armor;
+ int weapon;
+ int ammo;
+ int ammo_shells;
+ int ammo_nails;
+ int ammo_rockets;
+ int ammo_cells;
+ int items;
+ int pos[3]; // z=altitude (pos up)
+ int ang[3]; // 16bit; 0=tilt (negative up), 1=yaw (negative left)
+ int speed[4];
+ int speedlastthis;
+ int speedcnt;
+ char filter[16][16];
+ int filternum;
+
+ int diedatloc; // near what loc you died last time
+ int diedatown;
+ int diedatenemy;
+ int lastreportedloc;
+
+ int power; // powerups, smae bits as items
+ int lastpower; // same bits as items
+ int lastnonzeropower; // same bits as items
+
+ // history of actions
+ uchar lasttarget[64];
+ uchar lastpoint[64];
+ uchar lasttook[64];
+ int lastentitypicked;
+ int lastentitypointed;
+
+ // how many soundlists / modellists received
+ int cnt_sound;
+ int cnt_model;
+
+ int lastflag; //fire/jump
+ int impulsehistory[3];
+
+ int client_spectrack;
+ // level info
+ int changing;
+ int fortress;
+ int fpdcopy;
+ int dmm;
+ int originaldmm;
+ int dmmitemmask;
+ int ctf;
+ int fpd;
+#define FPD_PERCENT 0x01
+#define FPD_TIMERS 0x02
+#define FPD_SOUND 0x04
+#define FPD_PERCENTE 0x20
+#define FPD_POINT 0x80
+#define FPD_SKIN 0x100
+#define FPD_COLOR 0x200
+#define FPD_ITEM 0x400 // not implemented right now
+ int wherecrc;
+ int models;
+ uchar *model[MAXMODELS];
+ int grenademodel;
+ int pack2model;
+ int sounds;
+ uchar *sound[MAXMODELS];
+ int explosionsound;
+ int camps;
+ Camp camp[MAXCAMP];
+ int campcnt[16]; // 3=ring, 4=pent, 5=quad
+ char gamedir[MAXSTR];
+ // dynamically allocated locations
+ int locations;
+ int locationsmax; // this many allocated
+ Location *loc;
+ uchar locname[MAXSTR];
+ // tracking smooth
+ int tr_pos[3];
+ int tr_ang[3];
+ int tr_postar[3];
+ int tr_angtar[3];
+ // other players info (indexed by entity, not player number)
+ uchar playername[34][MAXPLAYERNAME];
+ uchar playerskin[34][MAXSKINNAME];
+ uchar playerproxy[34][MAXPROXYNAME];
+ uchar playerteamtext[34][8];
+ int playerfps[34];
+ int playerteam[34];
+ int playerbcolor[34];
+ int playertcolor[34];
+ int playerxcolor[34];
+ int playerrcolor[34]; // rank color
+ int playerfrags[34];
+ int playertime[34];
+ int playerfragsbest; // leader frags
+ int playernear[34];
+ int playerglow[34]; // low 4 bits=weapon!?
+ int playeractive[34]; // 0 for spec / unused players
+ int playerpos[34][3];
+ int playerang[34][3];
+
+ int trackloc;
+ int tracklocind;
+ int playerloc[34];
+ int playerlastloc[34];
+
+ int numplayers; // 1+last active player
+ DWORD time_playerrocket[34]; // last time rocket fired
+ DWORD time_playerdied[34];
+ int lastenemymask; // powerups
+ // visible entities
+ char entitygib[MAXENTITIES];
+ char entityvisible[MAXENTITIES];
+ uchar * entityname[MAXENTITIES];
+ int entitypos[MAXENTITIES][3];
+ int entitymask[MAXENTITIES];
+
+ // statistics
+ int packetsendtime[STATNUM];
+ int packetrecvtime[STATNUM];
+ int packetchoke[STATNUM];
+ int packetserversizecnt;
+ int packetserversize[16];
+ int packetclientsizecnt;
+ int packetclientsize[16];
+ int cppsdrop,cppspass;
+ int outoforder;
+ int outoforder30;
+
+ // client sizes measured at output
+ int packetclientinsize[16]; // compressed
+ int packetclientoutsize[16]; // compressed
+ // server sizes measured at input
+ int packetserverinsize[16]; // compressed
+ int packetserveroutsize[16]; // compressed
+
+ int lastpacketrecv;
+
+ int bytestoserver;
+ int bytestoclient;
+ DWORD time_bytestosec;
+ int bytestoserversec1; // tmp counters
+ int bytestoclientsec1; // tmp counters
+ int bytestoserversec; // val for last full second
+ int bytestoclientsec; // val for last full second
+ int bytestoserversecmax; // val for last full second
+ int bytestoclientsecmax; // val for last full second
+};
+
+extern int litemode; // = FPD mask
+extern int hpymode;
+extern int usernum;
+extern int maxusernum;
+extern int newsocketmode;
+extern User user[MAXUSERS];
+
+typedef struct
+{
+ int type;
+#define USERINFO_REPLY 0x00
+#define USERINFO_REQUEST 0x01
+#define USERINFO_PICKUP 0x02
+#define USERINFO_EXTENDED 0x07
+#define USERINFO_ERROR 0xff
+ int requestfrom;
+ int requestlong;
+ // sent in short
+ int armor;
+ int armortype; // 0=none, 1=green, 2=yellow, 3=red
+ int health;
+ int bestweapon;
+ int bestammo;
+ int items;
+ int power;
+ // sent in long
+ int x,y,z;
+ // not sent right now
+ int weapon;
+ int ammo;
+
+ int own;
+ char *name;
+} Userinfo;
+
+void q_command(User *u,uchar *s);
+
+void q_connect(uchar *inbuf,int inbytes,SOCKADDR_IN *from); // data to proxy connect port
+void q_client(User *u,uchar *buffer,int bytes); // data to client
+void q_server(User *u,uchar *buffer,int bytes); // data to server
+void q_proxy(User *u,uchar *uffer,int bytes); // data to proxy (server before connect)
+
+int packetmatch(User *u,uchar *buffer);
+
+int c_islocalconnect(SOCKADDR_IN *a);
+void c_hexdump(uchar *msg,uchar *buffer,int bytes,User *u);
+User *c_finduser(SOCKADDR_IN *from);
+User *c_newuser(SOCKADDR_IN *from);
+void c_killuser(User *u);
+void c_killserver(User *u);
+void c_reopenserver(User *u);
+int c_sameaddress(SOCKADDR_IN *a,SOCKADDR_IN *b);
+void c_setaddress(SOCKADDR_IN *a,uchar *text);
+void c_textaddress(uchar *text,SOCKADDR_IN *a);
+void c_sendservermain(User *u,uchar *data,int bytes);
+void c_sendserver(User *u,uchar *data,int bytes);
+void c_sendclient(User *u,uchar *data,int bytes);
+void c_sendto(SOCKADDR_IN *a,uchar *data,int bytes);
+
+void logx(int level,uchar *txt,...);
+
+int intat(uchar *a);
+
+//-----------------------------------------------
+
+char *_first_(void);
+char *_last_(void);
+
+//------------- stuff in main.c -----------------
+
+extern User *globalu;
+extern int loglevel;
+extern int lognpackets;
+extern int oldloglevel;
+
+extern uchar *version;
+extern uchar *version2;
+extern uchar *versionname;
+extern uchar *versiontype;
+extern int filecrc[2];
+
+void expires_gettime(void);
+void expires_startup(void);
+void expires_check(void);
+
+void *mymalloc(int size);
+void myfree(void *p);
+
+void codecrc(char *salt,char *result); // 16 byte result
+void statecrc(User *u,char *result); // 16 byte result
+
+//------------- stuff in proxy.c -----------------
+
+extern int proxy_port;
+extern int challenge;
+extern uchar proxy_allowip[MAXSTR];
+extern uchar proxy_denyip[16][MAXSTR];
+extern int proxy_denyipnum;
+extern uchar proxy_name[MAXSTR];
+extern uchar proxy_password[MAXSTR];
+extern char quakepath[MAXSTR];
+extern int quakehandle;
+extern char quakeoptions[MAXSTR];
+extern int quakemodified;
+extern int gamespy;
+extern uchar tmpbuf[BUFSIZE];
+extern uchar outbuf[BUFSIZE];
+int handlesaying(User *u,Userinfo *ui,uchar **d0,uchar **s0);
+
+void writeheader(uchar *inbuf,int *seqthis,int *seqrecv,int *bitthis,int *bitrecv);
+void readheader(uchar *inbuf,int *seqthis,int *seqrecv,int *bitthis,int *bitrecv);
+
+//------------- stuff in misc.c -----------------
+
+void strxor80(char *t);
+void stror80(char *t);
+void strand80(char *t);
+int memcmp7f(char *a,char *b,int len);
+
+void synccamp(User *u,int sync,int num);
+
+void userinfofromuser(Userinfo *ui,User *u);
+void gotinfomessage(User *u,uchar *msg,int entity);
+void sendinfomessage(User *u,uchar *msg);
+void sendsetinfo(User *u,uchar *msg);
+void decryptinfo(Userinfo *ui,uchar *datain);
+uchar *encryptinfo(Userinfo *ui);
+
+int checksum(char *packetstart,char *data,int size);
+void saveparseerror(char *text,int pos,uchar *buf,int bytes);
+int funnyconvert(uchar *d,uchar *s,int nocolor);
+uchar *formatqstring(uchar *in);
+uchar *extractfield(uchar *dest,uchar *src,uchar *field);
+void funnyhelp(User *u);
+
+void q_growlocmax(User *u,int num);
+void q_setlocname(User *u,int i,char *name);
+void q_loadloc(User *u,uchar *mapname);
+void q_savelocs(User *u);
+
+void q_print(User *u,uchar *text);
+void q_printf(User *u,uchar *text,...);
+void q_explode(User *u,int type,int x,int y,int z);
+void q_print1(User *u,uchar *text);
+void q_stufftext(User *u,uchar *text);
+void q_servercmd(User *u,uchar *text);
+void q_centerprint(User *u,uchar *text);
+
+char *playerlocdirection(User *u,int pl);
+int teamint(uchar *a);
+int is_version(uchar *x);
+int is_timer(uchar *x);
+int is_modified(uchar *x);
+void createversiontext(User *u,uchar *txt);
+int modifyimpulse(User *u,int *impulse);
+uchar *findnear(User *u,int x,int y,int z,int *ind);
+uchar *findpoint(User *u,int x,int y,int z,int ax,int ay,int az,int targetmask,int flags,int *entitynum);
+#define POINT_ONLYNAME 1
+#define POINT_ONLYPOS 2
+#define POINT_ADDSKIN 4 // fortress only
+#define POINT_TEAMMATENUMBER 8
+#define POINT_DIRECTION 16
+#define POINT_SELF 32
+uchar *findnearestitem(User *u,int x,int y,int z,int targetmask);
+int text2color(char *p);
+char *color2text(int c);
+
+void rankchange(User *u,int i,int dooverride);
+void skinoverride(User *u,int i);
+void spectatenext(User *u,uchar *findname);
+void cl_clearstats(User *u);
+void cl_updatestat(User *u,int stat,int value);
+void cl_setinfo(User *u,uchar *attr,uchar *value);
+Camp *campadd(User *u,int entity,int *pos,int modelindex,int skin);
+char *makeentityname(User *u,int modelindex,int skin,int *mask);
+void reportfpd(User *u,int fpd);
+void setinfo(User *u,int i,char *attr,char *value);
+
+int floodprot(User *u);
+void dmmchange(User *u,int newdmm);
+void showcrcerror(int showinternal,int crc8,int crc24);
+//------------- stuff in cmd.c -----------------
+
+void q_help(User *u,uchar *param);
+void q_command(User *u,uchar *s);
+
+//------------- misc macros ---------------------
+
+#define SAMETEAM(x) ((!u->fortress && u->team==u->playerteam[x]) || (u->fortress && u->bcolor==u->playerbcolor[x]))
+// you in same team as player x
+#define NEWQW (u->version>=22)
+
+//------------- compress.c ---------------------
+
+void compresstest(User *u,uchar *buf,int bytes);
+void compress(User *u,uchar *inbuf,int *inbytes,int server);
+void uncompress(User *u,uchar *inbuf,int *inbytes,int server);
+
+void count_save(User *u);
+void count_load(User *u);
+
+#ifndef WIN32
+
+extern int mainloop_low(int);
+extern int memicmp(uchar *,uchar *,int);
+extern int strlwr(uchar *);
+extern int stricmp(uchar *,uchar *);
+
+#endif /* !WIN32 */
+
+
+
+
--- /dev/null
+zip %1 * -x *.obj -x *.exe\r