altmp / altv-js-module Goto Github PK
View Code? Open in Web Editor NEWJS module for alt:V Multiplayer. Powered by NodeJS & v8
License: MIT License
JS module for alt:V Multiplayer. Powered by NodeJS & v8
License: MIT License
Client/server version
1.0-dev16
Current behavior
It's v40
Expected behavior
It's v41
Steps to reproduce
https://cdn.altv.mp/js-module/dev/x64_linux/modules/js-module/
Context (environment)
Can't use dev branch since last update.
[01:46:27][Error] Failed to load module "/mnt/d/Projekty/oesis-altv/build/linux/modules/js-module/libjs-module.so"
Module uses incompatible SDK version(v40) while v41 is required
Possible solution
Upload files to CDN
await import( './file' )
or import( './file' ).then().catch().finally()
only works after the connectionComplete event got fired.
import * as alt from 'alt-client';
async function loadBeforeConnectionComplete() {
alt.log( "You can see me" );
await import( './lib/utils' );
alt.log( "You will never see me" );
}
async function loadAfterConnectionComplete() {
alt.log( "Hey, are you ok?" );
await import( './lib/utils' ); // if set to a const you can use any export
alt.log( "I'm fine!" );
}
loadBeforeConnectionComplete();
alt.on("connectionComplete", () => {
loadAfterConnectionComplete();
});
File 2 can be just empty. The import/exports are working, as soon the event got triggered.
Make it work on startup.
No response
Windows
Current/release
client
Describe the bug
when you set a setInterval this destroy after long time run server (4 - 5 hour)
(only tested on server)
To Reproduce
make setInterval and wait a long time without interaction with you game or server
My interval
import { onClient, setInterval, emitClient } from 'alt-server'
setInterval(() => {
moduleAntiSpam = {}
}, 3500)
Environment:
When there's some huge processes on the workers, the game freeze completely.
They're correctly working on other threads:
[00:51:16] generator 0 done
[00:51:16] generator 1 done
[00:51:16] generator 4 done
[00:51:16] generator 2 done
[00:51:16] generator 3 done
[00:51:30] worker 5 done
[00:51:37] worker 8 done
[00:51:46] worker 6 done
[00:51:57] worker 7 done
[00:52:09] worker 9 done
When workers are done, the game is no longer freeze
worker.js
alt.on('sort', (array, id) => {
array.sort();
alt.log('worker', id, 'done');
})
alt.on('generate', (length, id) => {
let numbers = [];
for (let n = 0; n < length; n++) {
numbers.push(Math.floor(Math.random() * 500000));
}
alt.log('generator', id, 'done');
alt.emit('numbers', numbers);
})
client.ts
const NB_WORKERS: number = 10;
let workers: any[] = [];
for (let i = 0; i < NB_WORKERS; i++) {
workers.push(await this.createWorker(i));
}
for (let index = 0; index < workers.length / 2; index++) {
const generatorId = index;
const generator = workers[generatorId];
const workerId = index + workers.length / 2;
const worker = workers[workerId];
generator.on('numbers', (numbers: number[]) => {
worker.emit('sort', numbers, workerId);
})
generator.emit('generate', 5000000, generatorId)
}
createWorker(i)
create a worker and start it, then a promise is resolved when it loaded
Game not freeze and the main thread can work
No response
Windows 11
dev/7.0-dev10
client
The client crash on when stopping a resource containing a webview instanciated.
Destroying the webview before stopping the resource doesn't make the client crash.
import * as alt from 'alt-client';
new alt.WebView('https://github.com');
type the following in the server console
stop resourceName
The client shouldn't crash
No response
Windows 10.0.19042
dev/7.0-dev13
Worker events are called for no reason if event subscribers were registered in the load event
import * as alt from "alt-client"
// can be empty file
const worker = new alt.Worker("./worker.js")
worker.start()
worker.on("load", () => {
alt.log("worker ~gl~load")
worker.on("im called for no reason", (data) => {
alt.log("im called for no reason data:", JSON.stringify(data))
})
})
These events should not be triggered for no reason
No response
Windows 10
dev/7.0-dev21
client
E.g. a while(true)
loop can easily crash the client currently, because it will just block the main thread forever.
After a timeout of e.g. 5 seconds of synchronous code running it should be interrupted, and then resumed in the next tick (And show a warning / error)
Use RequestInterrupt
from the V8 API.
No response
No response
No response
client
Sending alt:V objects that can only exist on one side to the other side (for e.g. send alt.VoiceChannel
server->client) leads to a crash
alt.emitClientRaw(player, 'test', new alt.VoiceChannel(false, 0))
alt.emitRaw('test', new alt.VoiceChannel(false, 0)) // dont crash btw
Just a error in console without crash would be more user-friendly xd
No response
Windows 10
dev/8.0-dev2
shared
The client crashes if you write code like the one below.
alt.onServer('text', ...arr => { });
Im not sure if it is syntactically correct to write it like this, but at least the client should not crash.
alt.onServer('text', (...arr) => { });
Is totally fine.
Windows 10
9.0-rc2
client
Implement the V8 debugger in the client to allow the users to debug client scripts
Add the whole V8 debugger to the client so e.g. the Chrome DevTools can connect to it to debug clientside scripts
No response
An old implementation I did can be found in my fork in the debugger
branch here
https://github.com/leonmrbonnie/altv-client-js/tree/debugger
(The code for this is horrendous, but it shows how it works)
No response
client
Is your feature request related to a problem? Please describe.
Currently, you are limited to a single thread on clientside. (As normal in an JS environment)
For performance heavy clientside usages, like an entity streamer or similiar, it may be very useful to be able to run JS code on an external thread, to not block the main thread where the main script logic is running.
Describe the solution you'd like
API proposal (unfinished)
// alt-client
class Worker {
static readonly maxWorkers: number;
readonly valid: boolean;
readonly filePath: string;
readonly isPaused: boolean;
constructor(file: string);
// Event based messages between main thread and worker thread
emit(eventName: string, ...args: any[]): void;
on(eventName: string, handler: (...args: any[]) => void): void;
once(eventName: string, handler: (...args: any[]) => void): void;
// Built-in events
on(eventName: "load", handler: () => void): void;
on(eventName: "error", handler: (error: string) => void): void;
// Async based messages between main thread and worker thread
//send(messageName: string, ...args: any[]): Promise<any>;
//receive(messageName: string, handler: (...args: any[]) => any | Promise<any>): void;
start(): void;
destroy(): void;
pause(): void;
resume(): void;
}
// Globally available functions in worker thread
interface AltWorker {
// Event based messages
emit(eventName: string, ...args: any[]): void;
on(eventName: string, handler: (...args: any[]) => void): void;
once(eventName: string, handler: (...args: any[]) => void): void;
// Logging
log(...args: any[]): void;
logWarning(...args: any[]): void;
logError(...args: any[]): void;
// Timers
setTimeout(handler: (...args: any[]) => void, miliseconds: number): number;
setInterval(handler: (...args: any[]) => void, miliseconds: number): number;
nextTick(handler: (...args: any[]) => void): number;
clearTimeout(id: number): void;
clearInterval(id: number): void;
clearNextTick(id: number): void;
// Async based messages
//send(messageName: string, ...args: any[]): Promise<any>;
//receive(messageName: string, handler: (...args: any[]) => any | Promise<any>): void;
}
declare var alt: AltWorker;
Describe alternatives you've considered
The current alternative is to use the WebView JS thread as an external thread, this is highly unpractical and as every WebView is its own CEF process, very wasteful.
Additional context
Every worker would be run in it's own C++ thread, with its own associated V8 isolate.
When a worker sends an event to the main thread, it should be added to a queue, which is emptied in the resource OnTick, and the appropriate handler callbacks for it should be called.
In a similiar fashion, when a callback is sent from the main thread to the worker, the workers tick (which will probably just be a while(true) loop) will execute the callback from its queue in the worker thread.
When this is fully working and implemented, I also suggest adding an async API to send messages with return values, instead of the event based approach as used everywhere. But this is just a little sugarcoating for the API, that can be added in the future after this API is fully fleshed out.
Every message should always only have one handler, and if more than one handler is added to a single message, an error should be thrown.
The total amount of available workers should be capped, because too many isolates will also decrease the performance of the main thread isolate, so a static property on the Worker class should be available, indicating the maximum amount of workers. A good value for that, can be researched later.
Each worker should be able to set a callback that is called right before it is destroyed, to clean up resources / send unfinished data to the main thread. Right after this callback is called, the whole worker thread isolate is shut down, and the thread is exited.
When the worker is created, a new thread is started to execute the worker, as this runs independently from the main thread, the worker may not be fully loaded when the class instance is created, as such the worker should emit the load
event (similiar to how it works for WebViews) once it is fully loaded and accepts events, if an event is sent before the worker is fully loaded, an error should be thrown telling the user to wait for the load
event before sending events to the worker.
Like the BaseObject valid
property, the worker should have the same property, which is true
by default, and is only changed to false
when .destroy()
is called on the worker and it has been shut down.
I am open to hear suggestions and discussion about this feature in this issue
See repro steps
console.log('start import')
// this promise will never be resolved
const kek = await import('whatever')
console.log('end import')
.
No response
Windows 11
dev/10.0-dev3
client
JS source maps do not work
For Source maps to work
N/A
N/A
7.0-dev10
client
If you try to start a worker, the game will crash.
switch to dev branch
import * as alt from 'alt-client'; const worker = new alt.Worker('./worker.js'); worker.start();
import * as alt from 'alt-worker'; console.log('hello');
The game shouldn't crash.
No response
Windows 10
dev/9.0-dev10
client
Describe the bug
everytick stop after a while
(only tested on server)
To Reproduce
make everytick and wait
My everytick
let lastTime = Date.now()
everyTick(() => {
const newTime = Date.now()
if (newTime - lastTime >= 3500) {
moduleAntiSpam = {}
lastTime = Date.now()
}
})
OS: Debian 11
alt:V server version: 3.0-dev14
Other installed modules: csharp-module
JS module version: 3.0-dev14
Describe the bug
The resourceStop
event emitted by the JS module when the resource stops, seems to stop working when there are multiple resources active.
To Reproduce
Steps to reproduce the behavior:
Just test the event with a single resource active and with multiple resources active.
Expected behavior
The event should be emitted correctly.
Screenshots
If applicable, add screenshots to help explain your problem.
Environment:
Happens everywhere.
Additional context
CNodeResourceImpl.cpp
Line 126 in Stop()
V8ResourceImpl.h
(helpers) Line 94 in DispatchStopEvent()
See reproduction steps
import * as alt from 'alt-server'
class MyVehicle extends alt.Vehicle {
test () {
return true
}
}
// this breaks the prototype after resource restart
alt.Vehicle.all
alt.Vehicle.prototype = MyVehicle.prototype
const veh = new alt.Vehicle('sultan2', 10000, 0, 0, 0, 0, 0)
// undefined after resource restart
console.log('veh.test():', veh.test?.())
// false after resource restart
console.log('veh instanceof MyVehicle:', veh instanceof MyVehicle)
// will be false too
console.log('veh instanceof alt.Vehicle:', veh instanceof alt.Vehicle)
// [ Entity {}, Entity {} ] after resource restart
console.log(alt.Vehicle.all)
Prototype overwrite should work as usual after each restart
Possible workaround
alt.on('resourceStop', () => {
alt.Vehicle.all.forEach(v => v.destroy())
})
or set prototype explicitly on each object:
alt.Vehicle.all.forEach(v => Object.setPrototypeOf(v, alt.Vehicle.prototype))
Windows 10
dev/9.0-dev8
shared
If you define a style property on a RmlElement by using .style['property']
, it affects the whole document.
On any rmlElement:
function setColor(id: string, color: string) {
let rmlElement = this.textDocument.getElementByID(id);
if (rmlElement != null) {
rmlElement.style['color'] = color;
}
}
Ability to access and set a css property by using .style
Using setProperty
works
Manjaro 21.2.1
9.0-rc2
client
When sending an array client->worker or worker->client an error may occur in the console and the game will freeze and crash a little later without a crashdump
[Error] Failed to deserialize worker event argument
[worker] received data: undefined
[Error] [Worker] [undefined:0] Error: Unable to deserialize cloned data due to invalid or unsupported version.
// client
import * as alt from "alt-client"
const worker = new alt.Worker("./worker.js")
worker.on("load", () => worker.emit("test", [1, 2, 3]))
// worker
import * as alt from "alt-worker"
alt.on("test", (data) => {
alt.log("[worker] received data:", data)
})
No errors and crashes
This problem may apply to all binary serialization
Windows 10
dev/7.0-dev20
shared
The "load" event is called before the worker is actually loaded/ready. When you try to emit something to the worker inside the load event, it won't send it.
When waiting for a few seconds with eg a timeout, it works fine.
Create a worker, emit in load event.
The worker to be ready when it sends load event.
No response
Windows
release 8.8
shared, client
Optimize the functions that use a callback and thus need a Locker to work.
Instead of creating a locker, the promise should be resolved with the result in the next tick.
Use either RunOnNextTick
helper from the module or try looking into the Microtask
API from V8 (e.g. EnqueueMicrotask
)
No response
No response
No response
client
Add the ability to use a base64 string in file paths for workers / dynamic imports, that loads that base64 string as code, instead of a file.
Should also be available for alt.evalModule
let result = alt.evalModule("data:text/javascript;base64,J2EnIDwgJ2In"); // "'a' < 'b'" as base64
Check if string starts with data:text/javascript;base64,
, then the text after that is the actual base64 string.
No response
This is particularly useful for the usage with bundlers.
No response
client
Since the nodeJS 17 update (tested and doesn't happen on release) when you stop the server via CTRL + C the terminal is frozen and you have to kill the terminal.
start an alt:V server in a terminal and stop the server process with CTRL + C
No response
Windows 10
dev/10.0-dev7
server
[21:03:31] [854] test log string: 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
[21:03:31] [855] test log string: H�
[21:03:31] [856] test log string: H�
[21:03:31] [857] test log string: H�
[21:03:31] [858] test log string: H�
[21:03:31] [859] test log string: H�
let logStr = ''
for (let i = 0; i < 860; i++) {
logStr += '1'
if (i > 853) {
alt.log(`[${i}] test log string:`, logStr)
}
}
Logging should be fine, as in the release branch
No response
Windows 10
dev/7.0-dev10
client
Hey,
so when I set the player meta serverside using alt.Player:setMeta
to be an empty array []
and get it using alt.Player:getMeta
it returns an empty object {}
instead of an empty array.
Cheers.
For Testing:
player.setMeta('myMeta', []);
player.getMeta('myMeta');
{}
instead of []
It should return an empty array when an empty array was set on this key.
No response
Windows 10
release
server
When trying to send the minimum int64/long value -2^63 via alt.emitServer
in client code or IPlayer.Emit
in server code, the other end just receives a 0.
The same thing happens when sending the uint64/ulong value 2^63 from the js client to the server, but sending the same value from server to client works as expected (this asymmetrical behaviour is probably explained by the fact that the js client does not check whether it should use MValueInt
or MValueUInt
, it just always converts a BigInt
to MValueInt
).
alt.emitServer('test', BigInt("-9223372036854775808"), BigInt("-9223372036854775807"), BigInt("9223372036854775808"), BigInt("9223372036854775809"));
[ClientEvent("test:test")]
public void TestEvent(User user, long a, long b, ulong c, ulong d)
{
Console.WriteLine(a); // "0"
Console.WriteLine(b); // "-9223372036854775807"
Console.WriteLine(c); // "0"
Console.WriteLine(d); // "9223372036854775809"
}
Sending -2^63 and 2^63 should work as they are a valid values for long or ulong respectively.
No response
Windows 10
9.0-rc2
Currently it's not possible to call natives from a Worker Thread
Call native method asynchronously from a Worker Thread via the use of proxys to run the native on the main thread and return the value
The drawback would not be huge as there is a very little overhead emitting between the main thread and a worker thread
No response
No response
No response
client
Serverside static getter alt.Blip.routeColor throws baseobject error
try{ alt.Blip.routeColor } catch(e) { console.error(e) }
No error.
No response
Windows 10
dev/7.0-dev13
server
alt.emitRaw with an array of alt:V entities as arguments will result in the following error:
Error: Failed to serialize value at eval (eval at <anonymous> (client.js:11:2), <anonymous>:1:5)
when using this code:
alt.emitServerRaw("test", alt.Player.all);
alt.emitServerRaw("test", alt.Player.all);
.
No response
windows 10
8.0-dev4
shared
Use the Fast C API from V8
N/A
N/A
https://github.com/nodejs/node/blob/master/src/node_process_methods.cc#L426
devsnek/node@d8eef83
N/A
shared, server, client
main.ts
console.log("1");
import("./test");
test.ts
console.log("2");
export {}
1 then 2 should be logged
No response
Windows 10
dev10.0-dev2
client
Subscribing to all Events on Client/Server will crash the Client/Server
client.js:
import * as alt from "alt-client";
//client crash
alt.on((name, ...args) => {
alt.log("test");
});
server.js:
import * as alt from "alt-server";
//server crash
alt.onClient((event, player, args) => {
alt.log("test");
});
dont crash
No response
Windows 10 (21H2 - 19044.1486)
9.0-rc2 (rc)
/
/
No response
No response
No response
server
Describe the bug
pointblip in runtime expecting 4 paramter.
radiusblip in runtime giving error: TypeError: alt.RadiusBlip is not a constructor
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Giving error output
Screenshots
Environment:
Additional context
Currently, the V8Helpers.h
header includes almost all shared helper functions, this is highly unpractical because you can't include only some things and also changing this header causes almost the whole project to recompile.
Instead of using this giant header file, split it up into more smaller header files.
V8Helpers
namespace (don't use V8
, its easy to be confused with the v8
namespace)V8Helpers::Serialization::Serializer
)No response
No response
No response
shared
The current system of handling built-in alt:V events is prone to issues and name collisions because the system just registers these events under a pre-defined name.
To work around this issue, alt.on
should ONLY be for custom events, and the built-in events are handled in some other way, for example something like this alt.onPlayerConnect(() => {})
(This idea is still being worked on)
Also, the event arguments are passed individually to the function, which means: Its impossible to add new parameters to somewhere except the end, because it otherwise breaks all existing code.
Instead the event arguments of built-in events should be passed to the function as a single object containing all the event data. This may seem like it would complicate things, but because we are using modern JavaScript, we can use e.g. the object destructuring syntax, allowing code to look almost the same as before, but being a lot more flexible.
By doing this we will have to completely seperate the handling of built-in and custom events in the module, which will on one hand be a big change to the codebase, but on the other hand will also make the code more understandable (Seriously, try understanding how events currently work in the module by just looking at the source, it's all over the place) and easier to maintain.
When we first implement this new system, we will have to keep the old event system as it is but completely deprecate it for AT LEAST 2 release versions, because this change is breaking probably every script that was ever written for alt:V. Hell, maybe we are going to keep the old system forever just for compatibility sake, but this is something that has to be properly thought out once the implementation is finished and the new events system has been thoroughly tested.
Proposed typings:
// wip
No response
No response
No response
shared
Currently, we just execute the main file of that resource and run it. This causes the problem that we can not load any custom code before the resource code is executed.
Add bootstrapper code similar to serverside. There, load and run the main script after e.g. injecting custom code into the global context.
No response
Needed to move utility classes like Vector3 to JS code, instead of C++ bindings.
No response
client
When creating a WebView, only events which are emitted in a short timeframe right after the WebView has been created are queued up. All events which are emitted after this timeframe expired (but before an event handler has been registered in the WebView) are lost forever.
Create a WebView, emit an event right after WebView creation and another event with some delay (but before the WebView has loaded). The first event will get delivered as soon as the WebView has fully been loaded, the seconds event (if delay is big enough) will never be delivered.
I created a resource that sends a few events after WebView creation and logs them to the console as soon as they are received by the event listener:
eventtest.zip
You can see that some events are never delivered:
All events that got emitted before the WebView has loaded would be delivered (in correct order) as soon as the WebView has loaded.
No response
Windows 10
7.3
process.on events are duplicated (rarely on server startup) and server freezes after resource restart
process.on("uncaughtException", (error) => {
console.log("uncaughtException", error.stack);
});
setTimeout(() => { throw new Error("test") }, 0);
.
No response
Windows 11
dev/10.0-dev4
server
Describe the bug
MySQL queries in the alt:V environment are way slower than in a normal NodeJS environment.
To Reproduce
Do a MySQL query and log the time it takes, you will see that a simple query takes about 100ms, while in normal NodeJS it doesn't even take a single ms.
Expected behavior
MySQL queries should be the same as in normal NodeJS:
Screenshots
N/A
Environment:
Additional context
This problem was described here already: altmp/altv-issues#453
See repro steps
alt.log([{a: 10}, 1, 2, 3])
current output: [object Object],1,2,3
Arrays must be logged properly, just like objects
No response
Windows 11
dev/10.0-dev7
shared
As worker threads run independent from the main thread it is allowed to use the Atomics.wait
API inside them, unfortunately the embedder has to implement the logic for it themselves though.
Implement the Atomics.wait
API with the V8 methods provided.
No response
v8::Isolate::SetAtomicsWaitCallback
No response
client
The Blip
class is currently duplicated on both sides, instead of being in shared bindings
Move the Blip
class to shared
No response
No response
No response
shared, server, client
Is your feature request related to a problem? Please describe.
When using the NodeJS built-in console.log
it a) does not display the log in our wanted format (without timestamp) and b) heavy usage of console.log
's causes freezes (some issue with printing to stdout maybe?)
Describe the solution you'd like
Redirect the console.log calls to our own logger.
Describe alternatives you've considered
N/A
Additional context
It is important to make sure that it is not just overwritten as done on clientside. The NodeJS console.log
does several things under the hood that our logger does not do. Take a look into the NodeJS source to find out exactly what it does and replicate it will still logging it with our logger. (Could we overwrite the global logger with another logger instance that sends it to a custom stream?)
Implement MValueDict and MValueList bindings, so you can directly create these from JS.
They are meant to be used from applications where big arrays/objects are sent via events, and the serialization of a raw JS object would be much slower than directly creating the MValue when creating your object.
API proposal
// wip
N/A
No response
No response
shared
Describe the solution you'd like
The NodeJS version of the module should be updated to the current LTS.
Current module version is v12.4.0, newest LTS is v12.18.4
The module could even be updated to NodeJS v14+, but sticking with LTS will probably be safer.
Describe alternatives you've considered
Crying
Allow workers to use the SharedArrayBuffer
API from v8, to share raw data across multiple threads.
Main thread
// Assume a worker is already created somewhere and stored
// in the variable 'worker'
// Create a new shared array buffer
let buffer = new SharedArrayBuffer(200);
// Add the buffer to the worker
// Returns an index that can be used inside the worker to retrieve that buffer
let index = worker.addSharedBuffer(buffer);
// Send an event to the worker to get the buffer
worker.emit("getMyBuffer", index);
Worker thread
let myArrayBuffer = null;
alt.on("getMyBuffer", (index) => {
// Gets the shared buffer at the specified index
myArrayBuffer = alt.getSharedBuffer(index);
// Now the shared buffer can be interacted with as you normally would
});
No response
No response
No response
client
Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
Describe the solution you'd like
A clear and concise description of what you want to happen.
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
Additional context
Add any other context or screenshots about the feature request here.
Raw emit sends object that can only exist on one side (e.g. serverside blip) and then we get an array buffer with error log on the other side?
server
import alt from "alt-client";
const player = alt.Player.all[0];
player.emitRaw("test", new alt.PointBlip(player));
client
import alt from "alt-server";
alt.onServer("test", (data) => {
alt.log("test", data);
});
Raw emit should throw an error before sending the data
No response
Windows 11
dev/10.0-dev4
shared
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.