Browse Source

initial commit

IVogel 2 years ago
commit
aa6bfa0bcc
37 changed files with 3232 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 16 0
      .vscode/c_cpp_properties.json
  3. 7 0
      Cargo.toml
  4. 11 0
      example/Cargo.toml
  5. 46 0
      example/src/lib.rs
  6. 14 0
      server-plugin-derive/Cargo.toml
  7. 33 0
      server-plugin-derive/src/lib.rs
  8. 11 0
      server-plugin/Cargo.toml
  9. 1 0
      server-plugin/external/vtables-0.1.0/.cargo-ok
  10. 5 0
      server-plugin/external/vtables-0.1.0/.cargo_vcs_info.json
  11. 4 0
      server-plugin/external/vtables-0.1.0/.gitignore
  12. 23 0
      server-plugin/external/vtables-0.1.0/Cargo.toml
  13. 12 0
      server-plugin/external/vtables-0.1.0/Cargo.toml.orig
  14. 674 0
      server-plugin/external/vtables-0.1.0/LICENSE
  15. 40 0
      server-plugin/external/vtables-0.1.0/README.md
  16. 8 0
      server-plugin/external/vtables-0.1.0/rustfmt.toml
  17. 7 0
      server-plugin/external/vtables-0.1.0/src/lib.rs
  18. 1 0
      server-plugin/external/vtables_derive-0.1.0/.cargo-ok
  19. 5 0
      server-plugin/external/vtables_derive-0.1.0/.cargo_vcs_info.json
  20. 4 0
      server-plugin/external/vtables_derive-0.1.0/.gitignore
  21. 38 0
      server-plugin/external/vtables_derive-0.1.0/Cargo.toml
  22. 25 0
      server-plugin/external/vtables_derive-0.1.0/Cargo.toml.orig
  23. 674 0
      server-plugin/external/vtables_derive-0.1.0/LICENSE
  24. 2 0
      server-plugin/external/vtables_derive-0.1.0/README.md
  25. 8 0
      server-plugin/external/vtables_derive-0.1.0/rustfmt.toml
  26. 292 0
      server-plugin/external/vtables_derive-0.1.0/src/lib.rs
  27. 404 0
      server-plugin/external/vtables_derive-0.1.0/tests/tests.rs
  28. 180 0
      server-plugin/src/ifilesystem.rs
  29. 122 0
      server-plugin/src/inetworkstringtable.rs
  30. 106 0
      server-plugin/src/lib.rs
  31. 9 0
      server-plugin/src/platform/mod.rs
  32. 167 0
      server-plugin/src/platform/unix/baseplugin.rs
  33. 91 0
      server-plugin/src/platform/unix/inetworkstringtable.rs
  34. 5 0
      server-plugin/src/platform/unix/mod.rs
  35. 108 0
      server-plugin/src/platform/windows/baseplugin.rs
  36. 76 0
      server-plugin/src/platform/windows/inetworkstringtable.rs
  37. 1 0
      server-plugin/src/platform/windows/mod.rs

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/target
+Cargo.lock

+ 16 - 0
.vscode/c_cpp_properties.json

@@ -0,0 +1,16 @@
+{
+    "configurations": [
+        {
+            "name": "Linux",
+            "includePath": [
+                "${workspaceFolder}/**"
+            ],
+            "defines": [],
+            "compilerPath": "/usr/bin/clang",
+            "cStandard": "c17",
+            "cppStandard": "c++14",
+            "intelliSenseMode": "linux-clang-x64"
+        }
+    ],
+    "version": 4
+}

+ 7 - 0
Cargo.toml

@@ -0,0 +1,7 @@
+[workspace]
+
+members = [
+    "server-plugin",
+    "server-plugin-derive",
+    "example"
+]

+ 11 - 0
example/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "example"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+server-plugin = {path = "./../server-plugin"}
+server-plugin-derive = {path = "./../server-plugin-derive"}
+
+[lib]
+crate-type = ["cdylib"]

+ 46 - 0
example/src/lib.rs

@@ -0,0 +1,46 @@
+use std::{ffi::{CStr, CString, c_void}, error::Error};
+
+use server_plugin::{ServerPlugin, INetworkStringTableContainer, CreateInterfaceFn};
+use server_plugin_derive::create_interface;
+
+struct TestPlugin {
+    plugin_name: CString,
+    string_tables: Option<&'static INetworkStringTableContainer>
+}
+
+impl TestPlugin {
+    pub fn new() -> Self {
+        Self {
+            plugin_name: CString::new("TestPlugin").unwrap(),
+            string_tables: None,
+        }
+    }
+}
+
+impl ServerPlugin for TestPlugin {
+    fn load(&mut self, sys_appfactory: CreateInterfaceFn, _server_factorty: CreateInterfaceFn) -> Result<(), Box<dyn Error>> {
+        self.string_tables = INetworkStringTableContainer::from_factory(sys_appfactory).ok();
+        Ok(())
+    }
+    fn server_activate(&mut self, _edict_list: &mut [*mut c_void], _max_players: i32) {
+        let string_tables = self.string_tables.unwrap();
+        for string_table in (0..string_tables.get_num_tables()).map_while(|id| string_tables.get_table(id)) {
+            dbg!(string_table);
+        }
+        let table = string_tables.find_table("GModGameInfo").unwrap();
+        for entry in (0..table.get_num_strings()).map_while(|id| table.get_string(id)) {
+            dbg!(entry);
+        }
+    }
+    fn game_frame(&mut self, simulating: bool) {
+        dbg!("Hello there", simulating);
+    }
+    fn get_plugin_description(&mut self) -> &CStr {
+        self.plugin_name.as_ref()
+    }
+}
+
+#[create_interface]
+pub fn main() -> TestPlugin {
+    TestPlugin::new()
+}

+ 14 - 0
server-plugin-derive/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "server-plugin-derive"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = {version = "1.0", features = ["full"]}
+quote = "1.0.15"
+
+[dev-dependencies]
+server-plugin = {path = "./../server-plugin"}

+ 33 - 0
server-plugin-derive/src/lib.rs

@@ -0,0 +1,33 @@
+#![recursion_limit = "512"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use quote::quote;
+
+#[proc_macro_attribute]
+pub fn create_interface(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    let input = syn::parse_macro_input!(item as syn::ItemFn);
+
+    let ret = &input.sig.output;
+    let body = &input.block;
+    let attrs = &input.attrs;
+
+    let result = quote! {
+        #[no_mangle]
+        pub static mut g_ServerPlugin: *mut std::ffi::c_void = 0 as _;
+        #[no_mangle]
+        #[allow(unused_variables)]
+        pub unsafe extern "C" fn CreateInterface(version: *const i8, result: *mut i32) -> *mut std::ffi::c_void {
+            #(#attrs)*
+            fn inner() #ret {
+                #body
+            }
+            if g_ServerPlugin.is_null() {
+                g_ServerPlugin = std::mem::transmute(Box::new(server_plugin::BasePlugin::new(inner())))
+            }
+            g_ServerPlugin
+        }
+    };
+    result.into()
+}

+ 11 - 0
server-plugin/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "server-plugin"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+vtables = {path = "./external/vtables-0.1.0"}
+vtables_derive = {path = "./external/vtables_derive-0.1.0"}
+
+[build-dependencies]
+cc = "1.0"

+ 1 - 0
server-plugin/external/vtables-0.1.0/.cargo-ok

@@ -0,0 +1 @@
+ok

+ 5 - 0
server-plugin/external/vtables-0.1.0/.cargo_vcs_info.json

@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "cf5e8351f4828b6132ae81bb939b51c695bae1b3"
+  }
+}

+ 4 - 0
server-plugin/external/vtables-0.1.0/.gitignore

@@ -0,0 +1,4 @@
+/target
+**/*.rs.bk
+Cargo.lock
+.idea/

+ 23 - 0
server-plugin/external/vtables-0.1.0/Cargo.toml

@@ -0,0 +1,23 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "vtables"
+version = "0.1.0"
+authors = ["jan"]
+description = "Interact with C++ virtual method tables from Rust"
+homepage = "https://github.com/not-wlan/vtables"
+readme = "README.md"
+license = "GPL-3.0"
+repository = "https://github.com/not-wlan/vtables"
+
+[dependencies]

+ 12 - 0
server-plugin/external/vtables-0.1.0/Cargo.toml.orig

@@ -0,0 +1,12 @@
+[package]
+name = "vtables"
+version = "0.1.0"
+authors = ["jan"]
+edition = "2018"
+license = "GPL-3.0"
+repository = "https://github.com/not-wlan/vtables"
+homepage = "https://github.com/not-wlan/vtables"
+description = "Interact with C++ virtual method tables from Rust"
+readme = "README.md"
+
+[dependencies]

+ 674 - 0
server-plugin/external/vtables-0.1.0/LICENSE

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 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, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU 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.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.

+ 40 - 0
server-plugin/external/vtables-0.1.0/README.md

@@ -0,0 +1,40 @@
+# vtables
+Interact with C++ virtual method tables from Rust
+
+## Usage
+Clone both this and [vtables_derive](https://github.com/not-wlan/vtables_derive) and add the following to your Cargo.toml:
+
+```
+[dependencies]
+vtables = "0.1"
+vtables_derive = "0.1"
+```
+
+You can then use methods from the virtual method table like this:
+```
+use vtables::VTable;
+use vtables_derive::VTable;
+use vtables_derive::has_vtable;
+use vtables_derive::virtual_index;
+
+#[has_vtable]
+#[derive(VTable, Debug)]
+pub struct EngineClient {
+}
+
+#[allow(non_snake_case)]
+impl EngineClient {
+    #[virtual_index(5)]
+    pub fn GetScreenSize(&self, width: *mut i32, height: *mut i32) {}
+    
+    #[virtual_index(26)]
+    pub fn IsInGame(&self) -> bool {}
+
+    #[virtual_index(108)]
+    pub fn ExecuteClientCmd(&self, command: *const c_char) {}
+
+    #[virtual_index(113)]
+    pub fn ClientCmd_Unrestricted(&self, command: *const c_char) {}
+}
+```
+A field containing the virtual method table pointer will automatically be added to your structure. Support for multiple inheritance is untested.

+ 8 - 0
server-plugin/external/vtables-0.1.0/rustfmt.toml

@@ -0,0 +1,8 @@
+edition = "2018"
+format_macro_bodies = true
+format_strings      = true
+merge_imports       = true
+reorder_impl_items  = true
+reorder_imports     = true
+reorder_modules     = true
+wrap_comments       = true

+ 7 - 0
server-plugin/external/vtables-0.1.0/src/lib.rs

@@ -0,0 +1,7 @@
+pub trait VTable {
+    /// # Safety
+    ///
+    /// This calls function pointers from memory with arbitrary parameters.
+    /// This is just about as unsafe as it gets.
+    unsafe fn get_virtual<T: Sized>(&self, index: usize) -> T;
+}

+ 1 - 0
server-plugin/external/vtables_derive-0.1.0/.cargo-ok

@@ -0,0 +1 @@
+ok

+ 5 - 0
server-plugin/external/vtables_derive-0.1.0/.cargo_vcs_info.json

@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "ba0d4e6aa66c9c6b5f95b9fe1bc8d7d20e089c92"
+  }
+}

+ 4 - 0
server-plugin/external/vtables_derive-0.1.0/.gitignore

@@ -0,0 +1,4 @@
+/target
+**/*.rs.bk
+Cargo.lock
+.idea/

+ 38 - 0
server-plugin/external/vtables_derive-0.1.0/Cargo.toml

@@ -0,0 +1,38 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "vtables_derive"
+version = "0.1.0"
+authors = ["jan"]
+description = "procedural macros to interact with C++ virtual method tables from Rust"
+homepage = "https://github.com/not-wlan/vtables_derive"
+readme = "README.md"
+license = "GPL-3.0"
+repository = "https://github.com/not-wlan/vtables_derive"
+
+[lib]
+proc-macro = true
+[dependencies.proc-macro2]
+version = "1.0"
+
+[dependencies.quote]
+version = "1.0"
+
+[dependencies.syn]
+version = "1.0"
+features = ["full"]
+[dev-dependencies.static_assertions]
+version = "1"
+
+[dev-dependencies.vtables]
+path = "./../vtables-0.1.0"

+ 25 - 0
server-plugin/external/vtables_derive-0.1.0/Cargo.toml.orig

@@ -0,0 +1,25 @@
+[package]
+name = "vtables_derive"
+version = "0.1.0"
+authors = ["jan"]
+edition = "2018"
+license = "GPL-3.0"
+readme = "README.md"
+description = "procedural macros to interact with C++ virtual method tables from Rust"
+repository = "https://github.com/not-wlan/vtables_derive"
+homepage = "https://github.com/not-wlan/vtables_derive"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote = "1.0"
+proc-macro2 = "1.0"
+
+[dependencies.syn]
+version = "1.0"
+features = ["full"]
+
+[dev-dependencies]
+static_assertions = "1"
+vtables = "0.1"

+ 674 - 0
server-plugin/external/vtables_derive-0.1.0/LICENSE

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 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, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU 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.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.

+ 2 - 0
server-plugin/external/vtables_derive-0.1.0/README.md

@@ -0,0 +1,2 @@
+# vtables_derive
+Please see [here](https://github.com/not-wlan/vtables/tree/master) for documentation.

+ 8 - 0
server-plugin/external/vtables_derive-0.1.0/rustfmt.toml

@@ -0,0 +1,8 @@
+edition = "2018"
+format_macro_bodies = true
+format_strings      = true
+merge_imports       = true
+reorder_impl_items  = true
+reorder_imports     = true
+reorder_modules     = true
+wrap_comments       = true

+ 292 - 0
server-plugin/external/vtables_derive-0.1.0/src/lib.rs

@@ -0,0 +1,292 @@
+#![warn(clippy::pedantic)]
+extern crate proc_macro;
+
+use crate::proc_macro::TokenStream;
+use quote::{format_ident, quote, ToTokens};
+use std::ops::Deref;
+use syn::{
+    self,
+    parse::{Parse, ParseStream, Parser},
+    parse_macro_input,
+    spanned::Spanned,
+    Attribute, DeriveInput, ExprLit, Field,
+    Fields::Named,
+    FnArg, ItemFn, ItemStruct, ItemTrait, Lit, Meta, MetaList, NestedMeta, Pat, Result, Signature,
+    TraitItem, Type,
+};
+
+fn impl_vtable(ast: DeriveInput) -> TokenStream {
+    let DeriveInput {
+        ident, generics, ..
+    } = ast;
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    let gen = quote! {
+        impl #impl_generics VTable for #ident #ty_generics #where_clause {
+            unsafe fn get_virtual<__VirtualMethodType: Sized>(&self, index: usize) -> __VirtualMethodType {
+                let vtable = self.vtable as *const __VirtualMethodType;
+                vtable.add(index).read()
+            }
+        }
+    };
+
+    gen.into()
+}
+
+#[proc_macro_derive(VTable)]
+pub fn vtable_derive(input: TokenStream) -> TokenStream {
+    let ast = parse_macro_input!(input as DeriveInput);
+    impl_vtable(ast)
+}
+
+fn add_vtable_field(item_struct: &mut ItemStruct) {
+    let fields = if let Named(fields) = &mut item_struct.fields {
+        fields
+    } else {
+        // todo: https://docs.rs/syn/1.0.11/syn/struct.Error.html
+        panic!("You can only decorate with #[has_vtable] a struct that has named fields.");
+    };
+
+    let struct_already_has_vtable_field = fields
+        .named
+        .iter()
+        .any(|f| f.ident.as_ref().map_or(false, |i| i == "vtable"));
+
+    if struct_already_has_vtable_field {
+        return;
+    }
+
+    let vtable_field = Field::parse_named
+        .parse2(quote! { pub vtable: *mut *mut usize })
+        .expect("internal macro error with ill-formatted vtable field");
+
+    fields.named.insert(0, vtable_field);
+}
+
+fn has_repr_c(item_struct: &ItemStruct) -> bool {
+    // Look for existing #[repr(C)] variants, e.g.,
+    // #[repr(C)]
+    // #[repr(C, packed(4))]
+
+    let has = |meta: &Meta, ident| meta.path().get_ident().map_or(false, |i| i == ident);
+
+    item_struct
+        .attrs
+        .iter()
+        .filter_map(|a| {
+            a.parse_meta()
+                .ok()
+                .filter(|meta| has(meta, "repr"))
+                .and_then(|meta| match meta {
+                    Meta::List(MetaList { nested, .. }) => Some(nested),
+                    _ => None,
+                })
+        })
+        .flatten()
+        .any(|n| match n {
+            NestedMeta::Meta(meta) => has(&meta, "C"),
+            _ => false,
+        })
+}
+
+fn add_repr_c(item_struct: &mut ItemStruct) {
+    if has_repr_c(item_struct) {
+        return;
+    }
+
+    let mut repr_c = Attribute::parse_outer
+        .parse2(quote! { #[repr(C)] })
+        .expect("internal macro error with ill-formed #[repr(C)]");
+
+    item_struct.attrs.append(&mut repr_c);
+}
+
+#[proc_macro_attribute]
+pub fn has_vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    let mut parsed: ItemStruct = parse_macro_input!(item as ItemStruct);
+    add_vtable_field(&mut parsed);
+    add_repr_c(&mut parsed);
+    parsed.into_token_stream().into()
+}
+
+#[derive(Debug)]
+struct VirtualIndex {
+    pub index: usize,
+}
+
+impl Parse for VirtualIndex {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let index: ExprLit = input.parse()?;
+        if let Lit::Int(int) = &index.lit {
+            let idx: usize = int
+                .base10_parse()
+                .map_err(|_| syn::Error::new(int.span(), "Invalid integer"))?;
+            return Ok(VirtualIndex { index: idx });
+        }
+        Err(syn::Error::new(
+            index.span(),
+            "Couldn't parse virtual method index!",
+        ))
+    }
+}
+
+fn create_trait_object(item_trait: &mut ItemTrait) -> proc_macro2::TokenStream {
+    let mut methods = item_trait
+        .items
+        .iter()
+        .filter_map(|item| {
+            if let TraitItem::Method(method) = item {
+                // Find our pseudo-attribute to get the target index
+                let index = method
+                    .attrs
+                    .iter()
+                    .find(|attr| attr.path.is_ident("dyn_index"))
+                    .expect("Missing \"dyn_index\" attribute!");
+
+                return Some((&method.sig, index));
+            }
+            None
+        })
+        .map(|(sig, index)| {
+            let group: proc_macro2::Group = syn::parse2(index.tokens.clone()).unwrap();
+            let lit: ExprLit = syn::parse2(group.stream()).unwrap();
+            let index = match lit.lit {
+                Lit::Int(int) => int.base10_parse::<usize>().ok(),
+                _ => None,
+            }
+            .expect("Malformed vtable index");
+            (sig, index)
+        })
+        .collect::<Vec<_>>();
+
+    // Order the indices correctly for later calculations.
+    // This is also required for dedup.
+    methods.sort_by_key(|(_, index)| *index);
+    let length = methods.len();
+    // Detect duplicated indices
+    methods.dedup_by_key(|(_, index)| *index);
+    assert_eq!(length, methods.len());
+    assert!(!methods.is_empty());
+
+    let (_, last_index) = methods.last().unwrap();
+
+    // Assign a method to every index in the range 0 => last index we've seen
+    let methods = (0usize..=*last_index)
+        .map(|i| {
+            // TODO: Generate identifiers from function names?
+            // TODO: Maybe mark the stub functions as unused with an underscore?
+            let ident = format_ident!("call_{}", i);
+            if let Some((signature, _)) = methods.iter().find(|(_, index)| i == *index) {
+                let argtys = get_arguments(signature);
+                let retty = &signature.output;
+                // TODO: re-use original ABI? Fail on generics?
+                quote! { #ident: extern "thiscall" fn(#(#argtys),*) #retty}
+            } else {
+                // Generate dummy stub functions
+                quote! { #ident: extern "thiscall" fn(*mut ()) }
+            }
+        })
+        .collect::<Vec<_>>();
+
+    let name = item_trait.ident.to_string();
+    let name = format_ident!("{}Vtable", name);
+
+    quote! {
+        struct #name {
+            dtor: extern "thiscall" fn(*mut ()),
+            size: usize,
+            align: usize,
+            #(#methods),*
+        }
+    }
+}
+
+#[proc_macro_attribute]
+pub fn dyn_index(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    // This is just a dummy handler so that the compiler shuts up.
+    // The actual parsing is done by `dyn_glue`
+    item
+}
+
+#[proc_macro_attribute]
+pub fn dyn_glue(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    let mut parsed: ItemTrait = parse_macro_input!(item);
+    let vtable = create_trait_object(&mut parsed);
+    let mut stream = parsed.into_token_stream();
+    stream.extend(vtable);
+    stream.into()
+}
+
+fn get_arguments(sig: &Signature) -> Vec<Type> {
+    // A vector of all parameter types
+    sig.inputs
+        .iter()
+        .flat_map(|arg| {
+            if let FnArg::Typed(pat) = arg {
+                return Some(pat.ty.deref().clone());
+            }
+            None
+        })
+        .collect()
+}
+
+#[proc_macro_attribute]
+pub fn virtual_index(attr: TokenStream, item: TokenStream) -> TokenStream {
+    let index = parse_macro_input!(attr as VirtualIndex).index;
+
+    // Sort out all of these into separate variables since quote! doesn't support
+    // the . operator
+    let parsed: ItemFn = syn::parse(item.clone()).unwrap();
+    // The visibility level of an item: inherited or pub or pub(restricted).
+    let vis = &parsed.vis;
+    // A vector containing all attributes of this function
+    let attrs = &parsed.attrs;
+    // A function signature in a trait or implementation: unsafe fn
+    // initialize(&self).
+    let sig = &parsed.sig;
+    // Return type of a function signature.
+    let retty = &sig.output;
+
+    // Separate the types from the names in the function parameters.
+    // We need the types to define our new function and the names to call it.
+    // The self parameter is being ignored here since it's way easier to just
+    // hardcode it in the quote!
+
+    let argtys = get_arguments(sig);
+
+    // A vector of all parameter names
+    let args = &sig
+        .inputs
+        .iter()
+        .flat_map(|arg| {
+            if let FnArg::Typed(pat) = arg {
+                if let Pat::Ident(ident) = pat.pat.deref() {
+                    return Some(ident.ident.clone());
+                }
+            }
+            None
+        })
+        .collect::<Vec<_>>();
+
+    // TODO: Maybe we should pass the unsafe to the caller?
+    #[cfg(target_os = "windows")]
+    let gen = quote! {
+        #(#attrs)*
+        #vis #sig {
+            unsafe {
+                self.get_virtual::<extern "thiscall" fn(*const Self, #(#argtys),*) #retty>(#index)(self as *const Self, #(#args),*)
+            }
+        }
+    };
+    #[cfg(not(target_os = "windows"))]
+    let gen = quote! {
+        #(#attrs)*
+        #vis #sig {
+            unsafe {
+                self.get_virtual::<fn(*const Self, #(#argtys),*) #retty>(#index)(self as *const Self, #(#args),*)
+            }
+        }
+    };
+
+    gen.into()
+}

+ 404 - 0
server-plugin/external/vtables_derive-0.1.0/tests/tests.rs

@@ -0,0 +1,404 @@
+#![warn(clippy::pedantic)]
+#![allow(dead_code)]
+#![feature(abi_thiscall)]
+
+use core::{
+    ops::{Deref, Div, Mul},
+    ptr,
+};
+use static_assertions as sa;
+use std::{borrow::Cow, fmt::Display, os::raw::*};
+use vtables::VTable;
+use vtables_derive::{dyn_glue, dyn_index, has_vtable, virtual_index, VTable};
+
+#[has_vtable]
+#[derive(VTable, Debug)]
+struct EngineClient {
+    test_field: u64,
+}
+
+#[dyn_glue]
+trait EngineClientTrait {
+    #[doc = "abc"]
+    #[dyn_index(12)]
+    fn test_fn() -> usize;
+    #[dyn_index(2)]
+    fn test_fn_2() -> usize;
+    #[dyn_index(3)]
+    fn test_fn_3(t: usize);
+}
+
+// ================================================================================================
+// These structures will fail to compile if #[derive(VTable)] does not respect
+// their generics.
+
+#[has_vtable]
+#[derive(VTable)]
+struct StructWithLifetimes<'a, 'b, 'c> {
+    field_with_lifetime_a: Option<&'a u32>,
+    field_with_lifetime_b: Cow<'b, str>,
+    field_with_lifetime_c: &'c f64,
+}
+
+#[has_vtable]
+#[derive(VTable)]
+struct StructWithTypeGenerics<T, U, V> {
+    t: T,
+    u: U,
+    v: V,
+}
+
+#[has_vtable]
+#[derive(VTable)]
+struct StructWithLifetimesAndGenerics<'a, 'b, 'c, T, U, V: Clone> {
+    t: &'a T,
+    u: Option<&'b U>,
+    v: Cow<'c, V>,
+}
+
+#[has_vtable]
+#[derive(VTable)]
+struct StructWithLifetimesAndGenericsAndVTableField<'a, 'b, 'c, T, U, V: Clone> {
+    t: &'a T,
+    u: Option<&'b U>,
+    vtable: usize,
+    v: Cow<'c, V>,
+}
+
+#[has_vtable]
+#[derive(VTable)]
+struct StructWithLifetimesAndGenericsAndVTableFieldAndWhereClause<'a, 'b, 'c, T, U, V>
+where
+    T: Mul + Div,
+    U: AsRef<str> + Copy,
+    V: Deref<Target = T> + Clone + Send + Sync + Display,
+{
+    t: &'a T,
+    u: Option<&'b U>,
+    vtable: usize,
+    v: Cow<'c, V>,
+}
+// ================================================================================================
+
+// This structure will fail to compile if #[has_vtable] added another `vtable`
+// field.
+#[has_vtable]
+struct AlreadyHasVTableField<'a> {
+    vtable: u8,
+    foo: bool,
+    bar: f32,
+    baz: Option<&'a u8>,
+}
+
+impl Default for EngineClient {
+    fn default() -> Self {
+        Self {
+            vtable: ptr::null_mut(),
+            test_field: 0,
+        }
+    }
+}
+
+#[allow(non_snake_case)]
+impl EngineClient {
+    #[virtual_index(0)]
+    pub fn GetTestField(&self) -> u64 {}
+
+    #[virtual_index(1)]
+    pub fn MutateTestField(&mut self, new_value: u64) {}
+
+    #[virtual_index(5)]
+    pub fn GetScreenSize(&self, width: *mut i32, height: *mut i32) {}
+
+    #[virtual_index(26)]
+    pub fn IsInGame(&self) -> bool {}
+
+    #[virtual_index(108)]
+    pub fn ExecuteClientCmd(&self, command: *const c_char) {}
+
+    #[virtual_index(113)]
+    pub fn ClientCmd_Unrestricted(&self, command: *const c_char) {}
+}
+
+#[test]
+fn has_vtable_adds_vtable_field() {
+    // This function will fail to compile if #[has_vtable] does not add a `vtable`.
+    sa::assert_fields!(EngineClient: vtable);
+}
+
+#[test]
+fn derive_vtable_adds_get_virtual_method() {
+    // This function will fail to compile if #[derive(VTable)] did not add a
+    // `get_virtual(usize)` method.
+
+    let engine_client = EngineClient::default();
+
+    let _f = |i| {
+        type ExampleVirtualMethod = fn(&EngineClient, bool) -> f64;
+        unsafe { engine_client.get_virtual::<ExampleVirtualMethod>(i) };
+    };
+}
+
+#[test]
+fn virtual_index_retains_declared_methods() {
+    // This function will fail to compile if #[virtual_index(...)] fails to emit the
+    // method it decorates.
+
+    let engine_client = EngineClient::default();
+
+    macro_rules! verify {
+        ($method:ident) => {{
+            verify!($method,)
+        }};
+
+        ($method:ident, $($arg:ident),*) => {{
+            let _f = |$($arg),*| engine_client.$method($($arg),*);
+        }};
+    }
+
+    verify!(GetScreenSize, w, h);
+    verify!(IsInGame);
+    verify!(ExecuteClientCmd, command);
+    verify!(ClientCmd_Unrestricted, command);
+}
+
+fn new_vtable_with_one_function(
+    function_index: usize,
+    function_pointer: *mut usize,
+) -> Vec<*mut usize> {
+    // [null, null, null, ..., function_pointer, null, ...]
+    //   0     1     2          function_index   NUM_NULL_ENTRIES_AFTER_FUNCTION
+    const NUM_NULL_ENTRIES_AFTER_FUNCTION: usize = 3;
+    let num_total_entries = 1 + function_index + NUM_NULL_ENTRIES_AFTER_FUNCTION;
+    let mut vtable = vec![ptr::null_mut(); num_total_entries];
+    vtable[function_index] = function_pointer;
+    vtable
+}
+
+#[test]
+fn call_void_virtual_method() {
+    const WIDTH_ASSERT: i32 = 800;
+    const HEIGHT_ASSERT: i32 = 600;
+
+    unsafe fn get_screen_size_impl(_client: &EngineClient, width: *mut i32, height: *mut i32) {
+        *width = WIDTH_ASSERT;
+        *height = HEIGHT_ASSERT;
+    }
+
+    let mut vtable = new_vtable_with_one_function(5, get_screen_size_impl as _);
+
+    let engine_client = EngineClient {
+        vtable: vtable.as_mut_ptr(),
+        ..Default::default()
+    };
+
+    let mut width = 0;
+    let mut height = 0;
+    engine_client.GetScreenSize(&mut width, &mut height);
+
+    assert_eq!(width, WIDTH_ASSERT);
+    assert_eq!(height, HEIGHT_ASSERT);
+}
+
+#[test]
+fn call_virtual_method_to_return_internal_field() {
+    const FIELD_ASSERT: u64 = 0xCafeBabe;
+
+    fn get_test_field_impl(client: &EngineClient) -> u64 {
+        client.test_field
+    }
+
+    let mut vtable = new_vtable_with_one_function(0, get_test_field_impl as _);
+
+    let engine_client = EngineClient {
+        vtable: vtable.as_mut_ptr(),
+        test_field: FIELD_ASSERT,
+    };
+
+    assert_eq!(engine_client.GetTestField(), FIELD_ASSERT);
+}
+
+#[test]
+fn call_virtual_method_to_mutate_internal_field() {
+    const FIELD_ASSERT: u64 = 0xDeadBeef;
+
+    fn mutate_test_field_impl(client: &mut EngineClient, new_value: u64) {
+        client.test_field = new_value;
+    }
+
+    let mut vtable = new_vtable_with_one_function(1, mutate_test_field_impl as _);
+
+    let mut engine_client = EngineClient {
+        vtable: vtable.as_mut_ptr(),
+        test_field: 0,
+    };
+
+    assert_eq!(engine_client.test_field, 0);
+
+    engine_client.MutateTestField(FIELD_ASSERT);
+
+    assert_eq!(engine_client.test_field, FIELD_ASSERT);
+}
+
+#[test]
+fn check_that_derive_vtable_adds_repr_c() {
+    // To be honest, this test is flaky because it relies on struct layouts
+    // whose stability I'm not familiar with across platforms, rustc versions, etc.
+    // Ideally, there'd be a simpler way to just check if a struct is literally
+    // decorated with the #[repr(C)] attribute rather than going through this dance
+    // of looking for the effects of the attribute.
+
+    // Anyway, here's the theory:
+    // If #[has_vtable] didn't add #[repr(C)] to the struct,
+    // then we'd expect to see the Rust compiler laying out the fields according to
+    // #[repr(rust)]. This default layout should order the fields of the struct
+    // in decreasing alignment to minimize padding. With that knowledge, we can
+    // probe the struct using raw pointers to look for the unique values we
+    // initialize each field with.
+    // https://github.com/rust-lang/rust/pull/37429
+
+    macro_rules! z {
+        ($t:ty) => {{
+            core::mem::size_of::<$t>()
+        }};
+    }
+
+    {
+        // First, test the theory on an undecorated struct.
+        struct Control {
+            least_aligned: u8,
+            somewhat_aligned: u16,
+            most_aligned: u32,
+        }
+
+        let control = Control {
+            least_aligned: 42,
+            somewhat_aligned: 2019,
+            most_aligned: 0xCafeBabe,
+        };
+
+        /*
+            We expect the Rust compiler to layout `control` in memory as:
+
+            +0: 0xCafeBabe
+            +4: 2019
+            +6: 42
+            +7: 1 byte padding to satisfy alignment constraint of the entire struct.
+
+            i.e., decreasing alignment
+        */
+
+        unsafe {
+            // Verify the fields are in fact laid out in decreasing alignment.
+            let cursor = &control as *const _ as *const u8;
+            let mut offset = 0;
+
+            {
+                let cursor: *const u32 = cursor.add(offset).cast();
+                assert_eq!(*cursor, control.most_aligned);
+                offset += z!(u32);
+            }
+
+            {
+                let cursor: *const u16 = cursor.add(offset).cast();
+                assert_eq!(*cursor, control.somewhat_aligned);
+                offset += z!(u16);
+            }
+
+            {
+                let cursor: *const u8 = cursor.add(offset).cast();
+                assert_eq!(*cursor, control.least_aligned);
+                offset += z!(u8);
+            }
+
+            // 1 byte padding to align entire struct to a multiple of maximum alignment.
+            offset += 1;
+
+            assert_eq!(z!(Control), offset);
+        }
+    }
+
+    // Great, those tests passed. So the theory is okay to work with.
+    // Now let's decorate a struct with #[has_vtable] to see if it has the
+    // #[repr(C)] attribute. We shouldn't see any of the struct fields moving
+    // from their declaration order.
+
+    #[has_vtable]
+    #[derive(VTable)]
+    struct Control {
+        vtable: usize,
+
+        least_aligned: u8,
+        somewhat_aligned: u16,
+        most_aligned: u32,
+    }
+
+    let control = Control {
+        vtable: 0,
+
+        least_aligned: 42,
+        somewhat_aligned: 2019,
+        most_aligned: 0xCafeBabe,
+    };
+
+    /*
+        We expect #[repr(C)] to layout `control` in memory as:
+
+        +0:     0
+
+        +8:     42
+        +9:     1 byte padding to satisfy alignment requirement of 2 bytes for `somewhat_aligned`
+        +10:    2019
+        +12:    0xCafeBabe
+
+        i.e., declaration order with padding to satisfy alignment requirements
+    */
+
+    unsafe {
+        // Verify the fields are in declaration order.
+        let cursor = &control as *const _ as *const u8;
+        let mut offset = z!(usize); // skip past vtable usize
+
+        {
+            let cursor: *const u8 = cursor.add(offset).cast();
+            assert_eq!(*cursor, control.least_aligned);
+            offset += z!(u8);
+        }
+
+        {
+            offset += 1; // 1 byte padding
+            let cursor: *const u16 = cursor.add(offset).cast();
+            assert_eq!(*cursor, control.somewhat_aligned);
+            offset += z!(u16);
+        }
+
+        {
+            let cursor: *const u32 = cursor.add(offset).cast();
+            assert_eq!(*cursor, control.most_aligned);
+            offset += z!(u32);
+        }
+
+        assert_eq!(z!(Control), offset);
+    }
+
+    // Sanity check on alignment ordering.
+
+    macro_rules! a {
+        ($t:ty) => {{
+            core::mem::align_of::<$t>()
+        }};
+    }
+
+    let alignments = [a!(u8), a!(u16), a!(u32)];
+
+    assert_eq!(
+        *alignments.iter().min().unwrap(),
+        a!(u8),
+        "u8 should have the smallest alignment constraint."
+    );
+    assert_eq!(
+        *alignments.iter().max().unwrap(),
+        a!(u32),
+        "u32 should have the largest alignment constraint."
+    );
+}

+ 180 - 0
server-plugin/src/ifilesystem.rs

@@ -0,0 +1,180 @@
+use std::ffi::c_void;
+use std::ffi::CString;
+use std::mem;
+use std::os::raw::c_char;
+
+use vtables::VTable;
+use vtables_derive::has_vtable;
+use vtables_derive::virtual_index;
+use vtables_derive::VTable;
+
+use crate::CreateInterfaceFn;
+
+pub type FileHandle = i32;
+
+#[repr(i32)]
+#[allow(dead_code)]
+pub enum SeekType {
+    Head,
+    Current,
+    Tail,
+}
+
+#[allow(dead_code)]
+pub enum Open {
+    RB,
+    WB,
+    A,
+    ABPlus,
+}
+
+impl Into<*const c_char> for Open {
+    fn into(self) -> *const c_char {
+        match self {
+            Open::RB => b"rb\0".as_ptr() as _,
+            Open::WB => b"wb\0".as_ptr() as _,
+            Open::A => b"a\0".as_ptr() as _,
+            Open::ABPlus => b"ab+\0".as_ptr() as _,
+        }
+    }
+}
+
+#[has_vtable]
+#[derive(VTable, Debug)]
+pub struct IBaseFileSystem {}
+
+#[allow(dead_code)]
+impl IBaseFileSystem {
+    #[virtual_index(0)]
+    fn read_internal(&self, output: *mut u8, buffer_size: i32, file: FileHandle) -> i32 {}
+    #[virtual_index(1)]
+    fn write_internal(&self, input: *const u8, buffer_size: i32, file: FileHandle) -> i32 {}
+    #[virtual_index(2)]
+    fn open_internal(
+        &self,
+        file_name: *const c_char,
+        options: *const c_char,
+        path_id: *const c_char,
+    ) -> FileHandle {
+    }
+    #[virtual_index(3)]
+    pub fn close(&self, file: FileHandle) {}
+    #[virtual_index(4)]
+    pub fn seek(&self, file: FileHandle, offset: i32, seek_from: SeekType) {}
+    #[virtual_index(5)]
+    pub fn tell(&self, file: FileHandle) -> u32 {}
+    #[virtual_index(6)]
+    pub fn file_size(&self, file: FileHandle) -> usize {}
+    #[virtual_index(7)]
+    fn size_internal(&self, file_name: *const c_char, path_id: *const c_char) -> u32 {}
+    #[virtual_index(8)]
+    pub fn flush(&self, file: FileHandle) {}
+    #[virtual_index(9)]
+    fn precache_internal(&self, file_name: *const c_char, path_id: *const c_char) -> bool {}
+    #[virtual_index(10)]
+    fn file_exists_internal(&self, file_name: *const c_char, path_id: *const c_char) -> bool {}
+    #[virtual_index(11)]
+    fn is_file_writable_internal(&self, file_name: *const c_char, path_id: *const c_char) -> bool {}
+    #[virtual_index(12)]
+    fn set_file_writable_internal(
+        &self,
+        file_name: *const c_char,
+        writable: bool,
+        path_id: *const c_char,
+    ) -> bool {
+    }
+    #[virtual_index(13)]
+    fn get_file_time_internal(&self, file_name: *const c_char, path_id: *const c_char) -> u64 {}
+    #[virtual_index(16)]
+    fn unzip_file_internal(
+        &self,
+        file_name: *const c_char,
+        path: *const c_char,
+        destination: *const c_char,
+    ) -> bool {
+    }
+
+    pub fn read(&self, output: &mut [u8], file: FileHandle) -> i32 {
+        self.read_internal(output.as_mut_ptr(), output.len() as _, file)
+    }
+    pub fn write(&self, input: &[u8], file: FileHandle) -> i32 {
+        self.write_internal(input.as_ptr(), input.len() as _, file)
+    }
+    pub fn open(&self, path: &str, options: Open, game_path: Option<&str>) -> Option<FileHandle> {
+        let result = self.open_internal(
+            CString::new(path).map_or(0 as _, |path| path.as_ptr()),
+            options.into(),
+            game_path.map_or(0 as _, |path| {
+                CString::new(path).map_or(0 as _, |path| path.as_ptr())
+            }),
+        );
+        if result != 0 {
+            Some(result)
+        } else {
+            None
+        }
+    }
+    pub fn size(&self, path: &str, game_path: Option<&str>) -> usize {
+        self.size_internal(
+            CString::new(path).map_or(0 as _, |path| path.as_ptr()),
+            game_path.map_or(0 as _, |path| {
+                CString::new(path).map_or(0 as _, |path| path.as_ptr())
+            }),
+        ) as _
+    }
+    pub fn precache(&self, path: &str, game_path: Option<&str>) -> bool {
+        self.precache_internal(
+            CString::new(path).map_or(0 as _, |path| path.as_ptr()),
+            game_path.map_or(0 as _, |path| {
+                CString::new(path).map_or(0 as _, |path| path.as_ptr())
+            }),
+        )
+    }
+    pub fn file_exsists(&self, path: &str, game_path: Option<&str>) -> bool {
+        self.file_exists_internal(
+            CString::new(path).map_or(0 as _, |path| path.as_ptr()),
+            game_path.map_or(0 as _, |path| {
+                CString::new(path).map_or(0 as _, |path| path.as_ptr())
+            }),
+        )
+    }
+    pub fn is_file_writable(&self, path: &str, game_path: Option<&str>) -> bool {
+        self.is_file_writable_internal(
+            CString::new(path).map_or(0 as _, |path| path.as_ptr()),
+            game_path.map_or(0 as _, |path| {
+                CString::new(path).map_or(0 as _, |path| path.as_ptr())
+            }),
+        )
+    }
+    pub fn set_file_writable(&self, path: &str, writable: bool, game_path: Option<&str>) -> bool {
+        self.set_file_writable_internal(
+            CString::new(path).map_or(0 as _, |path| path.as_ptr()),
+            writable,
+            game_path.map_or(0 as _, |path| {
+                CString::new(path).map_or(0 as _, |path| path.as_ptr())
+            }),
+        )
+    }
+    pub fn get_file_time(&self, path: &str, game_path: Option<&str>) -> u64 {
+        self.get_file_time_internal(
+            CString::new(path).map_or(0 as _, |path| path.as_ptr()),
+            game_path.map_or(0 as _, |path| {
+                CString::new(path).map_or(0 as _, |path| path.as_ptr())
+            }),
+        )
+    }
+
+    pub fn from_ptr(ptr: *mut c_void) -> &'static IBaseFileSystem {
+        unsafe { mem::transmute(ptr) }
+    }
+
+    pub fn from_factory(factory: CreateInterfaceFn) -> Result<&'static IBaseFileSystem, i32> {
+        let mut result = 0;
+        let ptr = unsafe { factory(b"VBaseFileSystem011\0".as_ptr() as _, &mut result) };
+        if ptr.is_null() {
+            Err(result)
+        } else {
+            Ok(IBaseFileSystem::from_ptr(ptr))
+        }
+    }
+}

+ 122 - 0
server-plugin/src/inetworkstringtable.rs

@@ -0,0 +1,122 @@
+use std::ffi::c_void;
+use std::ffi::CStr;
+use std::ffi::CString;
+use std::fmt;
+use std::mem;
+
+use vtables::VTable;
+use vtables_derive::has_vtable;
+use vtables_derive::VTable;
+
+use crate::CreateInterfaceFn;
+
+#[has_vtable]
+#[derive(VTable)]
+pub struct INetworkStringTable {}
+
+impl fmt::Debug for INetworkStringTable {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("INetworkStringTable")
+            .field("table_id", &self.get_table_id())
+            .field("name", &self.get_table_name())
+            .field("num_strings", &self.get_num_strings())
+            .field("max_strings", &self.get_max_strings())
+            .field("entry_bits", &self.get_entry_bits())
+            .finish()
+    }
+}
+
+#[allow(dead_code)]
+impl INetworkStringTable {
+    pub fn get_table_name(&self) -> &CStr {
+        unsafe { CStr::from_ptr(self.get_table_name_internal()) }
+    }
+
+    pub fn get_string(&self, string_number: i32) -> Option<&CStr> {
+        let ptr = self.get_string_internal(string_number);
+        if ptr.is_null() {
+            None
+        } else {
+            unsafe { Some(CStr::from_ptr(ptr)) }
+        }
+    }
+
+    pub fn get_string_userdata(&self, string_number: i32) -> Option<Vec<u8>> {
+        let mut length = 0;
+        let ptr = self.get_string_userdata_internal(string_number, &mut length) as *const u8;
+        if ptr.is_null() {
+            None
+        } else {
+            let mut vec = vec![0u8; length as _];
+            for offset in 0..length as usize {
+                vec[offset] = unsafe { *ptr.add(offset) };
+            }
+            Some(vec)
+        }
+    }
+
+    pub fn set_string_userdata(&self, string_number: i32, data: &[u8]) -> Result<(), &'static str> {
+        if data.len() > 2i32.pow(self.get_entry_bits() as _) as _ {
+            Err("Data is too long")
+        } else {
+            self.set_string_userdata_internal(string_number, data.len() as _, data.as_ptr() as _);
+            Ok(())
+        }
+    }
+
+    pub fn from_ptr(ptr: *mut c_void) -> Option<&'static Self> {
+        unsafe { (ptr as *mut Self).as_ref() }
+    }
+}
+
+#[has_vtable]
+#[derive(VTable, Debug)]
+pub struct INetworkStringTableContainer {}
+
+#[allow(dead_code)]
+impl INetworkStringTableContainer {
+    pub fn create_string_table(
+        &self,
+        table_name: &str,
+        max_entries: i32,
+        userdatafixedsize: Option<i32>,
+        userdatanetworkbits: Option<i32>,
+        is_file_names: Option<bool>,
+    ) -> Option<&'static INetworkStringTable> {
+        let cstr = CString::new(table_name).unwrap_or_default();
+        let ptr = dbg!(self.create_string_table_ex_internal(
+            cstr.as_ptr(),
+            max_entries,
+            userdatafixedsize.unwrap_or(0),
+            userdatanetworkbits.unwrap_or(0),
+            is_file_names.unwrap_or(false),
+        ));
+        dbg!(ptr);
+        INetworkStringTable::from_ptr(ptr)
+    }
+
+    pub fn get_table(&self, string_table: i32) -> Option<&'static INetworkStringTable> {
+        unsafe { self.get_table_internal(string_table).as_ref() }
+    }
+
+    pub fn find_table(&self, table_name: &str) -> Option<&'static INetworkStringTable> {
+        unsafe {
+            let cstr = CString::new(table_name).unwrap_or_default();
+            self.find_table_internal(cstr.as_ptr()).as_ref()
+        }
+    }
+
+    pub fn from_ptr(ptr: *mut c_void) -> &'static Self {
+        unsafe { mem::transmute(ptr) }
+    }
+
+    pub fn from_factory(factory: CreateInterfaceFn) -> Result<&'static Self, i32> {
+        let mut result = 0;
+        let ptr = unsafe { factory(b"VEngineServerStringTable001\0".as_ptr() as _, &mut result) };
+        if ptr.is_null() {
+            Err(result)
+        } else {
+            Ok(Self::from_ptr(ptr))
+        }
+    }
+}

+ 106 - 0
server-plugin/src/lib.rs

@@ -0,0 +1,106 @@
+#![feature(abi_thiscall)]
+
+use std::ffi::{c_void, CStr};
+
+pub use platform::current::BasePlugin;
+
+mod ifilesystem;
+mod inetworkstringtable;
+mod platform;
+
+pub use inetworkstringtable::{
+    INetworkStringTable,
+    INetworkStringTableContainer
+};
+
+#[repr(i32)]
+#[derive(Debug, Clone, Copy)]
+pub enum PluginResult {
+    Continue,
+    Override,
+    Stop,
+}
+
+#[repr(i32)]
+#[derive(Debug, Clone, Copy)]
+pub enum QueryStatus {
+    ValueIntact,
+    CvarNotFound,
+    NotACvar,
+    CvarProtected,
+}
+
+pub type CreateInterfaceFn = unsafe extern "C" fn(*const i8, *mut i32) -> *mut c_void;
+
+#[allow(unused_variables)]
+pub trait ServerPlugin {
+    fn load(
+        &mut self,
+        game_factory: CreateInterfaceFn,
+        server_factory: CreateInterfaceFn,
+    ) -> Result<(), Box<dyn std::error::Error>> {
+        Ok(())
+    }
+    fn unload(&mut self) {}
+    fn pause(&mut self) {}
+    fn unpause(&mut self) {}
+    fn get_plugin_description(&mut self) -> &CStr;
+    fn level_init(&mut self, level_name: &CStr) {}
+    fn server_activate(&mut self, edict_list: &mut [*mut c_void], max_clients: i32) {}
+    fn game_frame(&mut self, simulating: bool) {}
+    fn level_shutdown(&mut self) {}
+    fn client_active(&mut self, edict: *mut c_void) {}
+    fn client_disconnect(&mut self, edict: *mut c_void) {}
+    fn client_put_in_server(&mut self, edict: *mut c_void, player_name: &CStr) {}
+    fn set_command_client(&mut self, client_index: i32) {}
+    fn client_settings_changed(&mut self, edict: *mut c_void) {}
+    fn client_connect(
+        &mut self,
+        edict: *mut c_void,
+        player_name: &CStr,
+        player_address: &CStr,
+    ) -> Result<PluginResult, String> {
+        Ok(PluginResult::Continue)
+    }
+    fn client_command(&mut self, edict: *mut c_void, args: *const c_void) -> PluginResult {
+        PluginResult::Continue
+    }
+    fn network_id_validated(&mut self, player_name: &CStr, network_id: &CStr) -> PluginResult {
+        PluginResult::Continue
+    }
+    fn on_query_cvar_value_finished(
+        &mut self,
+        cookie: i32,
+        edict: *mut c_void,
+        status: QueryStatus,
+        cvar_name: &CStr,
+        cvar_value: &CStr,
+    ) {
+    }
+    fn on_edict_allocated(&mut self, edict: *mut c_void) {}
+    fn on_edict_freed(&mut self, edict: *const c_void) {}
+}
+
+// struct TestPlugin {
+//     plugin_name: CString,
+// }
+
+// impl TestPlugin {
+//     fn new() -> Self {
+//         Self {
+//             plugin_name: CString::new("TestPlugin").unwrap(),
+//         }
+//     }
+// }
+
+// impl ServerPlugin for TestPlugin {
+//     fn get_plugin_description(&mut self) -> &CStr {
+//         self.plugin_name.as_ref()
+//     }
+// }
+
+// #[no_mangle]
+// #[allow(unused_variables)]
+// pub unsafe extern "C" fn CreateInterface(version: *const i8, result: *mut i32) -> *mut c_void {
+//     mem::transmute(Box::new(BasePlugin::new(TestPlugin::new())))
+// }

+ 9 - 0
server-plugin/src/platform/mod.rs

@@ -0,0 +1,9 @@
+// #[cfg(target_family="windows")]
+// pub mod windows;
+// #[cfg(target_family="windows")]
+// pub use windows as current;
+
+// #[cfg(target_family="unix")]
+pub mod unix;
+// #[cfg(target_family="unix")]
+pub use unix as current;

+ 167 - 0
server-plugin/src/platform/unix/baseplugin.rs

@@ -0,0 +1,167 @@
+use std::ffi::{c_void, CStr};
+
+use crate::{CreateInterfaceFn, PluginResult, QueryStatus, ServerPlugin};
+
+#[allow(dead_code)]
+pub struct BasePlugin<PLUGIN>
+where
+    PLUGIN: ServerPlugin,
+{
+    server_callbacks_vtable: *const *mut c_void,
+    server_plugin: PLUGIN,
+}
+
+#[allow(unused_variables)]
+impl<PLUGIN> BasePlugin<PLUGIN>
+where
+    PLUGIN: ServerPlugin,
+{
+    const PLUGIN_VTABLE: &'static [*mut c_void] = &[
+        BasePlugin::<PLUGIN>::load as _,
+        BasePlugin::<PLUGIN>::unload as _,
+        BasePlugin::<PLUGIN>::pause as _,
+        BasePlugin::<PLUGIN>::unpause as _,
+        BasePlugin::<PLUGIN>::get_plugin_description as _,
+        BasePlugin::<PLUGIN>::level_init as _,
+        BasePlugin::<PLUGIN>::server_activate as _,
+        BasePlugin::<PLUGIN>::game_frame as _,
+        BasePlugin::<PLUGIN>::level_shutdown as _,
+        BasePlugin::<PLUGIN>::client_active as _,
+        BasePlugin::<PLUGIN>::client_disconnect as _,
+        BasePlugin::<PLUGIN>::client_put_in_server as _,
+        BasePlugin::<PLUGIN>::set_command_client as _,
+        BasePlugin::<PLUGIN>::client_settings_changed as _,
+        BasePlugin::<PLUGIN>::client_connect as _,
+        BasePlugin::<PLUGIN>::client_command as _,
+        BasePlugin::<PLUGIN>::network_id_validated as _,
+        BasePlugin::<PLUGIN>::on_query_cvar_value_finished as _,
+        BasePlugin::<PLUGIN>::on_edict_allocated as _,
+        BasePlugin::<PLUGIN>::on_edict_freed as _,
+    ];
+    pub fn new(server_plugin: PLUGIN) -> Self {
+        Self {
+            server_callbacks_vtable: Self::PLUGIN_VTABLE.as_ptr(),
+            server_plugin,
+        }
+    }
+    #[inline(never)]
+    unsafe fn load(
+        &mut self,
+        game_factory: CreateInterfaceFn,
+        server_factory: CreateInterfaceFn,
+    ) -> bool {
+        match self.server_plugin.load(game_factory, server_factory) {
+            Err(e) => false,
+            _ => true,
+        }
+    }
+    #[inline(never)]
+    unsafe fn unload(&mut self) {
+        self.server_plugin.unload()
+    }
+    #[inline(never)]
+    unsafe fn pause(&mut self) {
+        self.server_plugin.pause()
+    }
+    #[inline(never)]
+    unsafe fn unpause(&mut self) {
+        self.server_plugin.unpause()
+    }
+    #[inline(never)]
+    unsafe fn get_plugin_description(&mut self) -> *const i8 {
+        self.server_plugin.get_plugin_description().as_ptr()
+    }
+    #[inline(never)]
+    unsafe fn level_init(&mut self, level_name: *const i8) {
+        self.server_plugin.level_init(CStr::from_ptr(level_name))
+    }
+    #[inline(never)]
+    unsafe fn server_activate(
+        &mut self,
+        edict_list: *mut c_void,
+        edict_count: i32,
+        max_clients: i32,
+    ) {
+        // TODO: Pass slice with acutal edicts.
+        self.server_plugin.server_activate(&mut [], max_clients)
+    }
+    #[inline(never)]
+    unsafe fn game_frame(&mut self, simulating: bool) {
+        self.server_plugin.game_frame(simulating);
+    }
+    #[inline(never)]
+    unsafe fn level_shutdown(&mut self) {
+        self.server_plugin.level_shutdown();
+    }
+    #[inline(never)]
+    unsafe fn client_active(&mut self, edict: *mut c_void) {
+        self.server_plugin.client_active(edict);
+    }
+    #[inline(never)]
+    unsafe fn client_disconnect(&mut self, edict: *mut c_void) {
+        self.server_plugin.client_disconnect(edict);
+    }
+    #[inline(never)]
+    unsafe fn client_put_in_server(&mut self, edict: *mut c_void, player_name: *const i8) {
+        self.server_plugin
+            .client_put_in_server(edict, CStr::from_ptr(player_name));
+    }
+    #[inline(never)]
+    unsafe fn set_command_client(&mut self, client_index: i32) {
+        self.server_plugin.set_command_client(client_index);
+    }
+    #[inline(never)]
+    unsafe fn client_settings_changed(&mut self, edict: *mut c_void) {
+        self.server_plugin.client_settings_changed(edict);
+    }
+    #[inline(never)]
+    unsafe fn client_connect(
+        &mut self,
+        allow_connect: *mut bool,
+        edict: *mut c_void,
+        player_name: *const i8,
+        player_address: *const i8,
+        message_buffer: *mut i8,
+        max_message_length: i32,
+    ) -> PluginResult {
+        match self.server_plugin.client_connect(
+            edict,
+            CStr::from_ptr(player_name),
+            CStr::from_ptr(player_address),
+        ) {
+            Err(msg) => {
+                *allow_connect = false;
+                message_buffer.copy_from(msg.as_bytes().as_ptr() as _, max_message_length as _);
+                PluginResult::Stop
+            }
+            Ok(res) => res,
+        }
+    }
+    #[inline(never)]
+    unsafe fn client_command(&mut self, edict: *mut c_void, args: *const c_void) -> PluginResult {
+        self.server_plugin.client_command(edict, args)
+    }
+    #[inline(never)]
+    unsafe fn network_id_validated(
+        &mut self,
+        player_name: *const i8,
+        network_id: *const i8,
+    ) -> PluginResult {
+        self.server_plugin
+            .network_id_validated(CStr::from_ptr(player_name), CStr::from_ptr(network_id))
+    }
+    #[inline(never)]
+    unsafe fn on_query_cvar_value_finished(
+        &mut self,
+        cookie: i32,
+        edict: *mut c_void,
+        status: QueryStatus,
+        cvar_name: *const i8,
+        cvar_value: *const i8,
+    ) {
+    }
+    #[inline(never)]
+    unsafe fn on_edict_allocated(&mut self, edict: *mut c_void) {}
+    #[inline(never)]
+    unsafe fn on_edict_freed(&mut self, edict: *const c_void) {}
+}

+ 91 - 0
server-plugin/src/platform/unix/inetworkstringtable.rs

@@ -0,0 +1,91 @@
+use std::ffi::c_void;
+use std::os::raw::c_char;
+
+use vtables::VTable;
+use vtables_derive::virtual_index;
+
+use crate::inetworkstringtable::{INetworkStringTable, INetworkStringTableContainer};
+
+impl INetworkStringTable {
+    #[virtual_index(2)]
+    pub(in crate) fn get_table_name_internal(&self) -> *const c_char {}
+    #[virtual_index(3)]
+    pub fn get_table_id(&self) -> i32 {}
+    #[virtual_index(4)]
+    pub fn get_num_strings(&self) -> i32 {}
+    #[virtual_index(5)]
+    pub fn get_max_strings(&self) -> i32 {}
+    #[virtual_index(6)]
+    pub fn get_entry_bits(&self) -> i32 {}
+    #[virtual_index(7)]
+    pub fn set_tick(&self, tick: i32) {}
+    #[virtual_index(8)]
+    pub fn changed_since_tick(&self, tick: i32) -> bool {}
+    #[virtual_index(9)]
+    pub(in crate) fn add_string_internal(
+        &self,
+        is_server: bool,
+        value: *const c_char,
+        length: i32,
+        data: *const c_void,
+    ) -> i32 {
+    }
+    #[virtual_index(10)]
+    pub(in crate) fn get_string_internal(&self, string_number: i32) -> *const c_char {}
+    #[virtual_index(11)]
+    pub(in crate) fn set_string_userdata_internal(
+        &self,
+        string_number: i32,
+        length: i32,
+        data: *const c_void,
+    ) {
+    }
+    #[virtual_index(12)]
+    pub(in crate) fn get_string_userdata_internal(
+        &self,
+        string_number: i32,
+        length: *mut i32,
+    ) -> *const c_void {
+    }
+}
+
+impl INetworkStringTableContainer {
+    #[virtual_index(2)]
+    pub(in crate) fn create_string_table_internal(
+        &self,
+        table_name: *const c_char,
+        max_entries: i32,
+        userdatafixedsize: i32,
+        userdatanetworkbits: i32,
+    ) -> *mut c_void {
+    }
+    #[virtual_index(3)]
+    pub fn remove_all_tables(&self) {}
+    #[virtual_index(4)]
+    pub(in crate) fn find_table_internal(
+        &self,
+        table_name: *const c_char,
+    ) -> *const INetworkStringTable {
+    }
+    #[virtual_index(5)]
+    pub(in crate) fn get_table_internal(&self, string_table: i32) -> *const INetworkStringTable {}
+    #[virtual_index(6)]
+    pub fn get_num_tables(&self) -> i32 {}
+    #[virtual_index(7)]
+    pub(in crate) fn create_string_table_ex_internal(
+        &self,
+        table_name: *const c_char,
+        max_entries: i32,
+        userdatafixedsize: i32,
+        userdatanetworkbits: i32,
+        is_file_names: bool,
+    ) -> *mut c_void {
+    }
+    #[virtual_index(8)]
+    pub(in crate) fn set_allow_clientside_add_internal(
+        &self,
+        table: *mut c_void,
+        allow_clientside_add_sring: bool,
+    ) -> *mut c_void {
+    }
+}

+ 5 - 0
server-plugin/src/platform/unix/mod.rs

@@ -0,0 +1,5 @@
+mod inetworkstringtable;
+pub use inetworkstringtable::*;
+
+mod baseplugin;
+pub use baseplugin::BasePlugin;

+ 108 - 0
server-plugin/src/platform/windows/baseplugin.rs

@@ -0,0 +1,108 @@
+use std::ffi::c_void;
+
+use crate::{CreateInterfaceFn, PluginResult, QueryStatus};
+
+#[allow(unused_variables)]
+impl BasePlugin {
+    fn new() -> Self {
+        Self {
+            server_callbacks_vtable: unsafe { SERVER_CALLBACKS_VTABLE.as_ptr() },
+            string_tables: None
+        }
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn load(
+        &mut self,
+        game_factory: CreateInterfaceFn,
+        server_factory: CreateInterfaceFn,
+    ) -> bool {
+        self.string_tables = INetworkStringTableContainer::from_factory(game_factory).ok();
+        true
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn unload(&mut self) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn pause(&mut self) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn unpause(&mut self) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn get_plugin_description(&mut self) -> *const i8 {
+        b"BasePlugin\0".as_ptr() as _
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn level_init(&mut self, level_name: *const i8) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn server_activate(
+        &mut self,
+        edict_list: *mut c_void,
+        edict_count: i32,
+        max_clients: i32,
+    ) {
+        let string_tables = self.string_tables.unwrap();
+        let lua_files = string_tables.find_table("client_lua_files").unwrap();
+        let _ = lua_files.set_string_userdata(0, &[]);
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn game_frame(&mut self, simulating: bool) {
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn level_shutdown(&mut self) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn client_active(&mut self, edict: *mut c_void) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn client_disconnect(&mut self, edict: *mut c_void) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn client_put_in_server(
+        &mut self,
+        edict: *mut c_void,
+        player_name: *const i8,
+    ) {
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn set_command_client(&mut self, client_index: i32) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn client_settings_changed(&mut self, edict: *mut c_void) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn client_connect(
+        &mut self,
+        allow_connect: *mut bool,
+        edict: *mut c_void,
+        player_name: *const i8,
+        player_address: *const i8,
+        message_buffer: *mut i8,
+        max_message_length: i32,
+    ) -> PluginResult {
+        dbg!(CStr::from_ptr(player_name), CStr::from_ptr(player_address));
+        PluginResult::Continue
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn client_command(
+        &mut self,
+        edict: *mut c_void,
+        args: *const c_void,
+    ) -> PluginResult {
+        PluginResult::Continue
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn network_id_validated(
+        &mut self,
+        player_name: *const i8,
+        network_id: *const i8,
+    ) -> PluginResult {
+        PluginResult::Continue
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn on_query_cvar_value_finished(
+        &mut self,
+        cookie: i32,
+        edict: *mut c_void,
+        status: QueryStatus,
+        cvar_name: *const i8,
+        cvar_value: *const i8,
+    ) {
+    }
+    #[inline(never)]
+    unsafe extern "thiscall" fn on_edict_allocated(&mut self, edict: *mut c_void) {}
+    #[inline(never)]
+    unsafe extern "thiscall" fn on_edict_freed(&mut self, edict: *const c_void) {}
+}

+ 76 - 0
server-plugin/src/platform/windows/inetworkstringtable.rs

@@ -0,0 +1,76 @@
+use std::ffi::c_void;
+use std::os::raw::c_char;
+
+use vtables::VTable;
+use vtables_derive::virtual_index;
+
+use crate::inetworkstringtable::{INetworkStringTable, INetworkStringTableContainer};
+
+impl INetworkStringTable {
+    #[virtual_index(1)]
+    pub(in crate) fn get_table_name_internal(&self) -> *const c_char {}
+    #[virtual_index(2)]
+    pub fn get_table_id(&self) -> i32 {}
+    #[virtual_index(3)]
+    pub fn get_num_strings(&self) -> i32 {}
+    #[virtual_index(4)]
+    pub fn get_max_strings(&self) -> i32 {}
+    #[virtual_index(5)]
+    pub fn get_entry_bits(&self) -> i32 {}
+    #[virtual_index(6)]
+    pub fn set_tick(&self, tick: i32) {}
+    #[virtual_index(7)]
+    pub fn changed_since_tick(&self, tick: i32) -> bool {}
+    #[virtual_index(8)]
+    pub(in crate) fn add_string_internal(
+        &self,
+        is_server: bool,
+        value: *const c_char,
+        length: i32,
+        data: *const c_void,
+    ) -> i32 {
+    }
+    #[virtual_index(9)]
+    pub(in crate) fn get_string_internal(&self, string_number: i32) -> *const c_char {}
+    #[virtual_index(10)]
+    pub(in crate) fn set_string_userdata_internal(&self, string_number: i32, length: i32, data: *const c_void) {}
+    #[virtual_index(11)]
+    pub(in crate) fn get_string_userdata_internal(&self, string_number: i32, length: *mut i32) -> *const c_void {}
+}
+
+impl INetworkStringTableContainer {
+    #[virtual_index(1)]
+    pub(in crate) fn create_string_table_internal(
+        &self,
+        table_name: *const c_char,
+        max_entries: i32,
+        userdatafixedsize: i32,
+        userdatanetworkbits: i32,
+    ) -> *mut c_void {
+    }
+    #[virtual_index(2)]
+    pub fn remove_all_tables(&self) {}
+    #[virtual_index(3)]
+    pub(in crate) fn find_table_internal(&self, table_name: *const c_char) -> *const INetworkStringTable {}
+    #[virtual_index(4)]
+    pub(in crate) fn get_table_internal(&self, string_table: i32) -> *const INetworkStringTable {}
+    #[virtual_index(5)]
+    pub fn get_num_tables(&self) -> i32 {}
+    #[virtual_index(6)]
+    pub(in crate) fn create_string_table_ex_internal(
+        &self,
+        table_name: *const c_char,
+        max_entries: i32,
+        userdatafixedsize: i32,
+        userdatanetworkbits: i32,
+        is_file_names: bool,
+    ) -> *mut c_void {
+    }
+    #[virtual_index(7)]
+    pub(in crate) fn set_allow_clientside_add_internal(
+        &self,
+        table: *mut c_void,
+        allow_clientside_add_sring: bool,
+    ) -> *mut c_void {
+    }
+}

+ 1 - 0
server-plugin/src/platform/windows/mod.rs

@@ -0,0 +1 @@
+pub mod inetworkstringtable;