Giter VIP home page Giter VIP logo

neosmodloader's Introduction

NeosModLoader

A mod loader for Neos VR. Consider joining our community on Discord for support, updates, and more.

Installation

If you are using the Steam version of Neos you are in the right place. If you are using the standalone version, read the Neos Standalone Setup instructions. If you are on Linux, read the Linux Notes.

  1. Download NeosModLoader.dll to Neos's Libraries folder (C:\Program Files (x86)\Steam\steamapps\common\NeosVR\Libraries).
  2. Place 0Harmony.dll into a nml_libs folder under your Neos install directory (C:\Program Files (x86)\Steam\steamapps\common\NeosVR\nml_libs). You will need to create this folder.
  3. Add mod DLL files to a nml_mods folder under your Neos install directory (C:\Program Files (x86)\Steam\steamapps\common\NeosVR\nml_mods). You can create the folder if it's missing, or simply launch Neos once with NeosModLoader installed and it will be created automatically.
  4. Add the following to Neos's launch options: -LoadAssembly Libraries\NeosModLoader.dll, substituting the path for wherever you put NeosModLoader.dll.
  5. Start the game. If you want to verify that NeosModLoader is working you can check the Neos logs. (C:\Program Files (x86)\Steam\steamapps\common\NeosVR\Logs). The modloader adds some very obvious logs on startup, and if they're missing something has gone wrong. Here is an example log file where everything worked correctly.

If NeosModLoader isn't working after following those steps, take a look at our troubleshooting page.

Example Directory Structure

Your Neos directory should now look similar to the following. Files not related to modding are not shown.

<Neos Install Directory>
│   Neos.exe
│   NeosLauncher.exe
│
├───Logs
│       <Log files will generate here>
│
├───nml_mods
│       InspectorScroll.dll
│       MotionBlurDisable.dll
│       NeosContactsSort.dll
|       <More mods go here>
├───nml_libs
│       0Harmony.dll
|       <More libs go here>
│
└───Libraries
        NeosModLoader.dll

Note that the libraries can also be in the root of the Neos install directory if you prefer, but the loading of those happens outside of NML itself.

Finding Mods

A list of known mods is available in the Neos Mod List. New mods and updates are also announced in our Discord.

Frequently Asked Questions

Many questions about what NML is and how it works are answered on our frequently asked questions page.

Making a Mod

Check out the Mod Creation Guide.

Configuration

NeosModLoader aims to have a reasonable default configuration, but certain things can be adjusted via an optional config file.

Contributing

Issues and PRs are welcome. Please read our Contributing Guidelines!

Licensing and Credits

NeosModLoader is licensed under the GNU Lesser General Public License (LGPL). See LICENSE.txt for the full license.

Third-party libraries distributed alongside NeosModLoader:

Third-party libraries used in source:

neosmodloader's People

Contributors

art0007i avatar banane9 avatar dependabot[bot] avatar doublestyx avatar eia485 avatar kazu0617 avatar ljoonal avatar psychpsyo avatar raemien avatar xdelta avatar zkxs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

neosmodloader's Issues

Improve safety of mod configuration save

Currently, NML nukes the existing mod configuration file as it writes a new one. If something goes wrong, this can result in an empty or partially written file. Relevant code here.

If this fails it's a pretty bad experience for the user, as NML will have all sorts of errors trying to load the broken configs, so not only has the user lost their configuration, they've also got to go manually delete the broken files.

This configuration save process could be made much more robust by writing to an intermediate file and then only on success copying that over the real config file. For example, using a temporary MyMod.tmp.json file:

  1. Delete MyMod.tmp.json if it exists
  2. Write new config to MyMod.tmp.json
  3. Optionally, verify that MyMod.tmp.json is contains valid JSON
  4. Delete MyMod.json
  5. Move MyMod.tmp.json to MyMod.json

There may be other ways of doing this—I've just suggested one possibility. For example, if C# has an API somewhere that can atomically replace an existing file (like a unix-style mv) that'd be even better.

SettingSync should be able to read mod settings

It'd very nice if the SettingSync component could read mod settings by accessing a path like ModLoader.modname.setting
This would make editing mod settings more straightforward and consistent with base game settings.

Neos Mod Loader not starting correctly on Windows Headless Host/Client

Came across this issue a few weeks ago but never got around to lodging it as an issue on the GitHub.

Logs indicate that the Headless Client is partly able to start the mod loader but some internal fault is not allowing it to get any further?

If needed I can elaborate further or copy my messages from the Discord Help & Support tab in the server.

[Feature Request] Hot-reloading DLLs

This would make mod development easier because we would not need to fully close and reopen Neos every time we make a change. It could also pave the way for automatic mod updates inside of Neos.

As far as I know, the main thing stopping this is that in C# you cannot load the DLL again if it contains the same class name. A way around this could be for NML to randomize the class names somehow. This was discussed a little with @art0007i already.

Could potentially be used when launching NML in Debug mode instead of enabling it by default.

NML2

A list of changes that would be neat to have whenever a breaking change is done:

  • Move the mod info to standard attributes
  • Add a mod description attribute? People seem to want this.
  • Expose a convenient helper getter for a mod specific HarmonyInstance in NeosMod (which could be lazily initialized on first access)
  • Unloading/reloading of mods (could always unpatch said harmony instance)
  • Determine if any mod actually uses the semver configs, axe it if it's unused.
  • Move the IncompatibleConfigurationHandlingOption method into the builder. It's just chilling all alone in the NeosMod class now.
  • Just generally cleanup everything
  • Properly handle nulls.
  • Remove deprecated APIs
  • Investigate if switching to dotnet6 would be beneficial
  • do a sweep of mandatory/optional parameters for various APIs and make sure it all makes sense
  • split mod config key name into separate fields for serialized name and display name
    • serialized name would be what's used in the json
    • display name can be shown in config managers
    • could be done via the form of a general metadata system with a few "known" keys or sumthing
  • Config validators
    • For example min/max support for numeric configs. People probably want to use this to make sliders.
    • Perhaps checking a combination of conflicting configs to ensure they're not enabled at once?
  • let applications skip potentially expensive string building for debug logs if debug isn't even enabled
    • Could be done via a Func<string> that handles building the text
    • Could be done via simply exposing an IsDebugEnabled() method
  • migrate from Newtonsoft.Json to System.Text.Json to fix the normal and headless clients needing distinct builds

@zkxs has also added stuff to this

NML "tell" via loaded types

LogiX can be used to make a user write a type such as NeosModLoader.NeosMod, NeosModSettings.NeosModSettings, etc into a TypeField and checking if it succeeded. This can be used to detect if users are running NML.

Headless Setup

Hey!

Just a little heads up,

Might be useful to include a NeosHeadless Installation Guide along side the normal one, due the Linux NeosHL not containing the "Libraries" Folder.

Screenshot 2022-08-14 224444

a way to specify nml config using launch arguments

when developing mods I often use the visual studio launch configurations to launch the game, it would be useful if I could somehow make debug=true only active when launching using visual studio, I think a launch argument pointing to a nml config file would work well but any other solution would work too

BadImageFormatException loading unmanged DLL from nml_libs

12:40:26 PM.941 (  0 FPS)	[INFO] [NeosModLoader] loading assemblies from nml_libs
12:40:27 PM.497 (  0 FPS)	[ERROR][NeosModLoader] error loading assembly from D:\Neos\app\nml_libs\Magick.Native-Q16-x64.dll: System.BadImageFormatException: 
File name: 'D:\Neos\app\nml_libs\Magick.Native-Q16-x64.dll'
  at (wrapper managed-to-native) System.Reflection.Assembly.LoadFrom(string,bool)
  at System.Reflection.Assembly.LoadFrom (System.String assemblyFile) [0x00000] in <9577ac7a62ef43179789031239ba8798>:0 
  at NeosModLoader.AssemblyLoader.LoadAssembly (System.String filepath) [0x00039] in <12833d5d0793412ca8252021544de084>:0 
12:40:27 PM.612 (  0 FPS)	[ERROR][NeosModLoader] error loading assembly from D:\Neos\app\nml_libs\git2-106a5f2.dll: System.BadImageFormatException: 
File name: 'D:\Neos\app\nml_libs\git2-106a5f2.dll'
  at (wrapper managed-to-native) System.Reflection.Assembly.LoadFrom(string,bool)
  at System.Reflection.Assembly.LoadFrom (System.String assemblyFile) [0x00000] in <9577ac7a62ef43179789031239ba8798>:0 
  at NeosModLoader.AssemblyLoader.LoadAssembly (System.String filepath) [0x00039] in <12833d5d0793412ca8252021544de084>:0 

I'm pretty sure this is because the way we load nml_libs doesn't work for unmanaged, aka native DLLs. Loading the unmanaged DLLs from the Neos install directory appears to work fine.

Newtonsoft.Json version mismatch on headless

This is presumably being caused by the Neos headless server shipping with a different Newtonsoft.Json version

Discord thread: https://discord.com/channels/901126079857692714/96729959321999772

On NML 1.9.1 and 1.9.0:

Exception getting types from assembly NeosModLoader, Version=2022.4.23.366, Culture=neutral, PublicKeyToken=null:
System.Reflection.ReflectionTypeLoadException: 要求された型のうち 1 つまたは複数を読み込めませんでした。詳細については、LoaderExceptions プロパティを 取得してください。
   場所 System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
   場所 System.Reflection.Assembly.GetTypes()
   場所 FrooxEngine.EngineInitializer.InitializeFrooxEngine(Engine engine) 場所 C:\Sync\Projects\Software\Applications\NeoS\NeosFramework\FrooxEngine\Initialization\EngineInitializer.cs:行 48

   at void FrooxEngine.EngineInitializer.InitializeFrooxEngine(Engine engine) in C:/Sync/Projects/Software/Applications/NeoS/NeosFramework/FrooxEngine/Initialization/EngineInitializer.cs:line 48
   at async Task FrooxEngine.Engine.Initialize(string appPath, string dataPath, string cachePath, ISystemInfo systemInfo, IInternalResource resources, bool verboseInit) in C:/Sync/Projects/Software/Applications/NeoS/NeosFramework/FrooxEngine/Engine.cs:line 717
   at void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<TStateMachine>(ref TStateMachine stateMachine)
   at Task FrooxEngine.Engine.Initialize(string appPath, string dataPath, string cachePath, ISystemInfo systemInfo, IInternalResource resources, bool verboseInit)
   at async Task FrooxEngine.StandaloneFrooxEngineRunner.Initialize(string dataPath, string cachePath, bool verbose) in C:/Sync/Projects/Software/Applications/NeoS/NeosFramework/FrooxEngine/Engine/StandaloneFrooxEngineRunner.cs:line 54
   at void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<TStateMachine>(ref TStateMachine stateMachine)
   at Task FrooxEngine.StandaloneFrooxEngineRunner.Initialize(string dataPath, string cachePath, bool verbose)
   at async Task NeosHeadless.Program.Main(string[] args) in C:/Sync/Projects/Software/Applications/NeoS/NeosFramework/NeosHeadless/Program.cs:line 177
   at void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<TStateMachine>(ref TStateMachine stateMachine)
   at Task NeosHeadless.Program.Main(string[] args)
   at void NeosHeadless.Program.<Main>(?)
        Reading settings file
...
User Spawn Userspace. Username: kaz0617, UserID: , MachineID: 89fdnqqpje-znlq4mjupag
Exception loading types from assembly: NeosModLoader, Version=2022.4.23.366, Culture=neutral, PublicKeyToken=null

   at List<AttributeMethod<A, D>> BaseX.ReflectionExtensions.FindAllStaticMethodsWithAttribute<A, D>(Predicate<Assembly> assemblyFilter)
   at void FrooxEngine.WorldPresets+<>c+<<GetPresets>b__13_0>d.MoveNext() in C:/Sync/Projects/Software/Applications/NeoS/NeosFramework/FrooxEngine/Userspace/WorldPresets/Scratchspace.cs:line 54
   at void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<TResult>.Start<TStateMachine>(ref TStateMachine stateMachine)
   at Task<IEnumerable<WorldPreset>> FrooxEngine.WorldPresets.GetPresets()+() => { }
   at bool System.Threading.ThreadPoolWorkQueue.Dispatch()
SessionID set to S-7b1f7717-5c91-4668-83f7-fdf5b1cfe4dc. LastModifyingUser:
...

Improvements for standalone usage

As some of you are probably already aware (we've talked a bit about this already on Discord), I'm currently developing a custom headless server for NeosVR that runs on a modern .NET runtime.

One of the features I'd like to offer out of the box is NML integration so that users won't have to download NML separately. Additionally, having NML shipped with the server would open up the possibility for many more integration features (such as mod queries and configuration via the server's REST API).

Today, NML works with the server in essentially the same way as it does with the stock headless client. I have bumped into a couple of things that would make integration a lot easier, however, and this issue is meant as a first collection of these so we can discuss which, if any, are something that can be changed or fixed on NML's side of things.

1. NML assumes the working directory is the NeosVR installation directory

This isn't a blocker per se, but it would be very useful to allow this value to be fed in or configured at runtime. Typically, server daemons run in restricted or atypical working directories for a variety of reasons, and right now the working directory must be forced to NeosVR's installation directory for NML to work.

2. NML assumes nml_mods and nml_libs can be created in the working directory

Same as above, really. Having the ability to configure these paths separately would also be a very good thing - I've seen users work around this by having symlinks to other locations in the filesystem, but that is not always a supported configuration (especially in a security-hardened context). For example, the server runs with strict restrictions on which paths can be written to as well as which paths executable code can be loaded from - if a user tries to symlink out into another location, that location may not fulfil the requirements by nml_mods and nml_libs from a hardening standpoint.

Allowing these paths to be configured or overridden would enable smarter integration with these types of configuration.

3. NML can only be loaded as a plugin

This one is a bit trickier, but being able to load NML explicitly and at a controlled point in the code instead of having to pass it as a LoadAssembly plugin would help a lot. Preferably, NML would support some kind of initialization routine that the plugin portion simply calls, and which could be used by the server application as well.

4. NML is only distributed via Github

Provided NML can be modified to support library-style initialization as described above, taking a dependency on NML as a library would be miles and miles easier if NML was distributed on something like nuget. This is obviously very much in the "nice to have" territory, and I fully understand if it's not in the cards. Using NML as a library is a niche use case at best, and setting up a nuget package for what is ostensibly one guy's mad idea would be a hard sell at the best of times :P

That said, publishing NML as a nuget package would be pretty easy and brings other benefits over time - dependency handling would be simplified for downstream consumers, for example.


Thanks for taking the time to read through this! Any comments, ideas, or questions are welcome.

Unable to save different types

When trying to use a type for ModConfigurationKey that has multiple values such as a Color or Float 4 they still work in game and change when you hit save but don't actually save to disk resulting the config file not being made/being made but missing relevant data.

image

10:04:11 AM.868 (135 FPS)       [ERROR][NeosModLoader/NeosModSettings] Failed to save Config for Test Plugin
10:04:11 AM.869 (135 FPS)       [ERROR][NeosModLoader/NeosModSettings] Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Normalized' with type 'BaseX.float2'. Path ''.
  at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerProperty) [0x00105] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonContainerContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.Serialization.JsonContract& memberContract, System.Object& memberValue) [0x000cd] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract collectionContract, Newtonsoft.Json.Serialization.JsonProperty containerProperty) [0x0008d] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonContract valueContract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerProperty) [0x000b0] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) [0x00079] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at Newtonsoft.Json.JsonSerializer.SerializeInternal (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) [0x0023a] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at Newtonsoft.Json.JsonSerializer.Serialize (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value) [0x00000] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at Newtonsoft.Json.Linq.JToken.FromObjectInternal (System.Object o, Newtonsoft.Json.JsonSerializer jsonSerializer) [0x0001c] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at Newtonsoft.Json.Linq.JToken.FromObject (System.Object o) [0x00006] in <e66c310ca5c34b7e936b87806e4f60f6>:0
  at NeosModLoader.ModConfiguration.Save (System.Boolean saveDefaultValues) [0x00093] in <67d6a64d7ebf403f83f1a8b1d8c03d22>:0
  at NeosModLoader.ModConfiguration.Save () [0x00000] in <67d6a64d7ebf403f83f1a8b1d8c03d22>:0
  at NeosModSettings.NeosModSettings+ModSettingsScreen.saveAllConfigs () [0x00047] in <0ceb8a3db94448f7869658db00c33bcc>:0

Attached above is the log that occurs when changing the default value of a float 2, the same thing happens for color, float4 etc

        public override string Name => "Test Plugin";
        public override string Author => "LeCloutPanda";
        public override string Version => "1.0.0";

        [AutoRegisterConfigKey]
        private static ModConfigurationKey<color> COLOR = new ModConfigurationKey<color>("Color", "", () => new color(0f, 0f, 0f, 0f));
        [AutoRegisterConfigKey]
        private static ModConfigurationKey<float4> FLOAT4 = new ModConfigurationKey<float4>("Float 4", "", () => new float4(0f, 0f, 0f, 0f));
        [AutoRegisterConfigKey]
        private static ModConfigurationKey<float3> FLOAT3 = new ModConfigurationKey<float3>("Float 3", "", () => new float3(0f, 0f, 0f));
        [AutoRegisterConfigKey]
        private static ModConfigurationKey<float2> FLOAT2 = new ModConfigurationKey<float2>("Float 2", "", () => new float2(0f, 0f));

        public static ModConfiguration config;

        public override void OnEngineInit()
        {
            config = GetConfiguration();
            config.Save(true);

            Msg(COLOR);
            Msg(FLOAT4);
            Msg(FLOAT3);
            Msg(FLOAT2);

            Harmony harmony = new Harmony("net.LeCloutPanda.TestPlugin");
            harmony.PatchAll();
        }

This is the code I used to test this with

Add a IncompatibleConfigurationHandlingOption for deleting specific broken keys

If I have an enum key set in my config. For example: "ENUM_KEY":"Test2" If I later remove the value Test2 from the enum in my code, it makes the mod fail to load because it can't find the value Test2 anymore. This can only be fixed by deleting the entire config or manually removing/fixing that key. Could there be a way for NML to do this automatically? Like for example it could detect the key that threw the error and just remove it. This would be a lot more ideal than nuking the whole config for me.

When using NML alongside other plugins, NML should not ruin the compatibility hash.

The compatibility hash is normally altered for each loaded plugin. Imagine a function hash that takes a list of plugins. For vanilla, you'd have hash(). For NML, you'd have hash(nml). For someone running multiple plugins, you'd have hash(myCoolPlugin, otherPlugin, veryGoodPlugin).

Currently, if NML is the only loaded plugin it spoofs the hash to be hash(), enabling multiplayer compatibility with vanilla users. However, if there is another plugin being loaded as well, then NML completely disables its spoofing and uses hash(NML, otherPlugin). This means that a otherPlugin user not running NML will have hash(otherPlugin) and therefore be incompatible with the NML + otherPlugin user.

NML should fix this by removing itself from the hash, but leaving any other plugins.

[Enhancement] Add config events for Reset, Save, Save All

I am developing a mod in which a user can define strings in their configuration. It would be helpful for me (and I imagine other developers) to know when a mod's configuration is/was saved. Likewise, this could be extended to the "Reset" and "Save all" operations as well, and implemented similarly to how OnThisConfigurationChanged and OnAnyConfigurationChanged works.

I am not aware of any similar/existing functionality already in NeosModLoader. If there is please let me know and I will amend the issue.

disable loading from subdirectories

I find this feature annoying more often than I find it useful. For example if I want to test something with only a single mod and temporarily disable every other mod I would just take all the current mods and throw them into a different folder. The easiest option would be just a subdirectory within mods called like "disabled" and then take them back out of there later.

I don't know if anyone at all uses the subdirectories feature at all. I assume it's there to allow better organization but it actually prevents me from organizing mods how I want (right now I need to put a disabled mods directory OUTSIDE the mods folder which gets kind of annoying to navigate).

A good middle ground would be to have some way to specify a folder shouldn't be searched by the mod loader so for example a dot. That way I could make a folder called ".disabled" and anything inside of there would be ignored, but also I think completely removing this feature is a good option, unless there are actually people who use that to organize their mods.

Installer

The manual installation steps aren't that bad, but we've seen a few people having trouble with getting files in the correct places. The obvious solution is to build an automatic installer.

Things this could do:

  • Set up the symlink hack on linux installs
  • Fix the issue with Windows blocking the dll files
  • Create a shortcut or batch file with the -LoadAssembly argument set up correctly

Standalone setup instructions assumes still using steam.

The Standalone Setup Instructions assume a player wants to use steam to launch still. We should include or reference Neos Standalone Build Docs for modloader setup without steam. This should start after step 5, as a If you'd like to still use steam use these instructions or if you'd like to keep it standalone (or quest without steam) use these instead.

This ties in with documentation improvements mentioned here neos-modding-group/neos-mod-manifest#135

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.