luna-rs / luna Goto Github PK
View Code? Open in Web Editor NEWA scalable and efficient Runescape server targeting revision #377
License: MIT License
A scalable and efficient Runescape server targeting revision #377
License: MIT License
Before implementing this completely I need be sure of it's implications on the current design of the region system and performance.
I might just add a flag to "luna.toml" that lets the user choose if they want to turn it on or off (smaller servers can leave it off, while larger servers might require it).
I tried running the server and got this
08 Feb 2017 23:58:27 io.luna.game.plugin.PluginBootstrap [LunaInitializationThread] ERROR: Catching java.lang.StackOverflowError: null at scala.tools.nsc.typechecker.Contexts$Context.bufferErrors(Contexts.scala:332) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Contexts$Context.reportErrors(Contexts.scala:333) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.silent(Typers.scala:672) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.normalTypedApply$1(Typers.scala:4524) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4580) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5343) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5360) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.transform.Erasure$Eraser.typed1(Erasure.scala:698) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5396) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5423) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5370) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5374) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5472) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5480) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.transform.Erasure$Eraser.adaptMember(Erasure.scala:644) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.transform.Erasure$Eraser.typed1(Erasure.scala:698) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5396) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5423) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5370) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5374) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$99.apply(Typers.scala:4525) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$99.apply(Typers.scala:4525) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.silent(Typers.scala:680) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.normalTypedApply$1(Typers.scala:4524) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4580) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5343) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5360) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.transform.Erasure$Eraser.typed1(Erasure.scala:698) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5396) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5423) ~[scala-compiler-2.11.8.jar:?]
I think this might be related to entities not showing up, but I could be dead wrong about that. The clients sends the command to open the door to Luna, but the door does not open. If this needs to be done in a Scala plugin, let me know and I might fiddle around with learning how to write one.
The Event interception looks wonky. This is partly due to syntactical limitations, but I still think we can do better. Improvements to it include
The result would be event interception looking something like the following examples
on('button_click, 1, 2, 3, 4) { msg =>
...
}
on('button_click) { msg =>
...
}
Or
on(ButtonClickEvent.is, 1, 2, 3, 4) { msg =>
...
}
on(ButtonClickEvent.is) { msg =>
...
}
You could even use pattern matching to get around the "parameter defined events" issue
on[ButtonClickEvent] match {
case 1 | 2 | 3 => println("Clicked button id #1, #2, or #3")
case 4 => println("Clicked button id #4")
case 5 => prinlnt("Clicked button id #5")
}
But I'm not really sure about this syntax because it isn't all that compact when you're only dealing with a single value
on[ButtonClickEvent] match {
case 1 => println("Clicked button id #1")
}
A good compromise would be to allow the compact syntax for 1-5 values, but any more than that and pattern matching must be used.
The ability to do something like "::hotfix login.scala" would provide many benefits. Why re-interpret potentially a hundred plugins just to update the 1, 2, or 3 plugins you modified?
I was thinking about how this could be done and I think it could be done by making the EventListeners hold the following data
Then the appropriate old EventListener could be swapped out for the new one. If that can't be done, a good compromise would be to just reload the pipelines of all the event types that the plugin file listens for.
A well seasoned Java/Scala programmer that sees ">>" is going to assume it's a bitwise operation. Therefore, I don't think that this should be associated with event interception.
Some better solutions to look into
Can you push the dependencies to the project? Would save others a lot of time getting set up.
Also is there a way to contact you in order to discuss other features and design?
When testing punishment commands recently, I noticed that mobs don't appear to players despite being in the same region. I haven't debugged or looked into this at all yet, but I assume it has something to do with this
Especially running, which just randomly stops even though the run button is on. Also does this weird thing where it steps back a few of your previous steps
I also need to confirm that the run energy algorithm works correctly (which I can't do until running is fixed)
Needed in order to finish off the Equipment
class. I might need to dump additional data from the wiki as well in order to accomplish this.
It's already implemented within the run energy algorithm but I need to add support for items in your inventory and equipment updating your weight value
I don't really like the idea of mixing and matching JSON/TOML
Because why would anyone want them included?
Hi,
This looks like a very interesting project and I would like to contribute. Could you make the repo build again?
Thanks!
post(Event evt)
and post(Event evt, Player plr)
seems a bit weird. The Player should be contained within the Event itself. This will also allow for more flexibility with matching.
Ryley added a nice implementation of it to Apollo, so I'll probably take a look at that to see how it works more in depth.
Hotfixing works by asynchronously reloading plugins into a new pipeline set, and then replacing the currently used pipeline set with it.
Replacing the currently used pipeline set must happen on the game thread to ensure thread safety.
Boon is the highest performing JSON library out there right now and follows a design similar to google-gson. SnakeYAML is a PITA to use, terribly designed, and with that I've come to the conclusion that it's just not worth using.
I've been looking into TOML lately and I like it but toml4j is the only library available for it and doesn't have serialization support. So I might be changing the data serialization format to TOML in the future when it does support serialization.
That can include donators, developers, veterans, etc.
Errors thrown by plugins are objectively a complete and total eyesore. They're unnecessarily bloated, and can look vastly different from one another depending on when the error is thrown (evaluation or application). Take for example, the following two different types of errors
Application error
21 Jul 2017 18:45:07 io.luna.game.event.EventListener [LunaGameThread RUNNING]
ERROR: Catching
io.luna.game.plugin.PluginFailureException: 2570 (of class java.lang.Integer)
at io.luna.game.event.EventListener.apply(EventListener.java:57) ~[classes/:?]
at io.luna.game.event.EventListenerPipeline.traverse(EventListenerPipeline.java:59) ~[classes/:?]
at io.luna.game.plugin.PluginManager.post(PluginManager.java:43) ~[classes/:?]
at io.luna.game.model.item.Equipment$EquipmentListener.sendEvent(Equipment.java:77) ~[classes/:?]
at io.luna.game.model.item.Equipment$EquipmentListener.onBulkUpdate(Equipment.java:62) ~[classes/:?]
at io.luna.game.model.item.ItemContainer.fireUpdateEvent(ItemContainer.java:832) ~[classes/:?]
at io.luna.game.model.item.ItemContainer.set(ItemContainer.java:717) ~[classes/:?]
at io.luna.game.model.item.Equipment.add(Equipment.java:263) ~[classes/:?]
at io.luna.game.model.item.ItemContainer.addAll(ItemContainer.java:248) ~[classes/:?]
at $line40.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$1(<console>:205) ~[?:?]
at $line40.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$1$adapted(<console>:199) ~[?:?]
at $line5.$read$$iw$$iw$$anon$1.accept(<console>:185) ~[?:?]
at io.luna.game.event.EventListener.apply(EventListener.java:49) ~[classes/:?]
at io.luna.game.event.EventListenerPipeline.traverse(EventListenerPipeline.java:59) ~[classes/:?]
at io.luna.game.plugin.PluginManager.post(PluginManager.java:43) ~[classes/:?]
at io.luna.game.model.mobile.Player.onActive(Player.java:279) ~[classes/:?]
at io.luna.game.model.Entity.setState(Entity.java:139) ~[classes/:?]
at io.luna.game.model.mobile.MobList.add(MobList.java:195) ~[classes/:?]
at io.luna.game.model.World.dequeueLogins(World.java:96) ~[classes/:?]
at io.luna.game.GameService.runOneIteration(GameService.java:78) ~[classes/:?]
at com.google.common.util.concurrent.AbstractScheduledService$ServiceDelegate$Task.run(AbstractScheduledService.java:188) ~[guava-22.0.jar:?]
at com.google.common.util.concurrent.Callables$4.run(Callables.java:122) ~[guava-22.0.jar:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_131]
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) ~[?:1.8.0_131]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) ~[?:1.8.0_131]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) ~[?:1.8.0_131]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_131]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_131]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
Caused by: scala.MatchError: 2570 (of class java.lang.Integer)
at $line47.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$2(<console>:211) ~[?:?]
at scala.runtime.java8.JFunction1$mcZI$sp.apply(JFunction1$mcZI$sp.java:12) ~[scala-library-2.12.2.jar:?]
at scala.Option.foreach(Option.scala:257) ~[scala-library-2.12.2.jar:?]
at $line47.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$1(<console>:209) ~[?:?]
at $line47.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$1$adapted(<console>:206) ~[?:?]
at $line5.$read$$iw$$iw$$anon$1.accept(<console>:185) ~[?:?]
at io.luna.game.event.EventListener.apply(EventListener.java:49) ~[classes/:?]
... 28 more
Evaluation error
21 Jul 2017 20:33:50 io.luna.game.plugin.PluginBootstrap [LunaInitializationThread]
ERROR: Catching
javax.script.ScriptException: unbound placeholder parameter in import io.luna.game.event.Event
import io.luna.game.event.impl.{ButtonClickEvent, EquipmentChangeEvent}
import io.luna.game.model.item.Equipment.RING
import io.luna.game.model.mobile.Player
private val RING_OF_STONE = 6583
private val STONE_MORPH = 2626
private val EASTER_RING = 7927
private val EGGS_MORPH = Vector(3689, 3690, 3691, 3692, 3693, 3694)
private def morph(plr: Player, msg: Event, to: Int) = {
(0 to 13).filterNot(_ == 3).foreach(plr.sendTabInterface(_, -1))
plr.sendForceTab(3)
plr.sendTabInterface(3, 6014)
plr.lockMovement
plr.transform(to)
msg.terminate
}
private def unmorph(plr: Player) = {
if (plr.inventory.computeRemainingSize > 1) {
plr.equipment.unequip(RING)
plr.displayTabInterfaces()
plr.unlockMovement
plr.transform(-1)
} else {
plr.sendMessage("You do not have enough space in your inventory.")
}
}
on[EquipmentChangeEvent] { msg =>
if (msg.index == RING) {
msg.newId.foreach {
case RING_OF_STONE => morph(msg.plr, msg, STONE_MORPH)
case EASTER_RING => morph(msg.plr, msg, rand(EGGS_MORPH))
case _ => _
}
}
}
// unmorph (figure out proper id)
onargs[ButtonClickEvent](23132) { msg => unmorph(msg.plr) } at line number 44 at column number 17
at scala.tools.nsc.interpreter.Scripted.$anonfun$compile$2(Scripted.scala:163) ~[scala-compiler-2.12.2.jar:?]
at scala.tools.nsc.interpreter.Scripted.$anonfun$compile$1(Scripted.scala:162) ~[scala-compiler-2.12.2.jar:?]
at scala.tools.nsc.interpreter.Scripted.withCompileContext(Scripted.scala:134) ~[scala-compiler-2.12.2.jar:?]
at scala.tools.nsc.interpreter.Scripted.compile(Scripted.scala:148) ~[scala-compiler-2.12.2.jar:?]
at scala.tools.nsc.interpreter.Scripted.eval(Scripted.scala:181) ~[scala-compiler-2.12.2.jar:?]
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) ~[?:1.8.0_131]
at io.luna.game.plugin.PluginBootstrap.initPlugins(PluginBootstrap.java:220) ~[classes/:?]
at io.luna.game.plugin.PluginBootstrap.init(PluginBootstrap.java:170) ~[classes/:?]
at io.luna.game.plugin.PluginBootstrap.call(PluginBootstrap.java:132) ~[classes/:?]
at io.luna.game.plugin.PluginBootstrap.call(PluginBootstrap.java:40) ~[classes/:?]
at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:111) ~[guava-22.0.jar:?]
at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:58) ~[guava-22.0.jar:?]
at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:75) ~[guava-22.0.jar:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_131]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_131]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
In order to begin the standardization of plugin errors, a few prerequisites must be met
And the new standard should look something like
ERROR: FAILED: 'my_plugin.scala'
CAUSED BY: unbound placeholder parameter
@ LINE 50: case _ => _
Not exactly sure what's wrong with it, when I test it nothing happens. Will have to look into it more
Blocked by issue #30
Blocked by issue #28 and will have to wait until toml4j supports writing them.
Luna doesn't have an official client, or even an unofficial one as far as I know. What do you guys use to run alongside the server?
I downloaded this repo and I can't compile it
It seems there are files missing
For example
Server.java
import io.luna.game.model.item.Shop;
But there is no such file
Same for
Bank.java
import io.luna.game.model.inter.StaticInventoryInterface;
Currently if one were to invoke the GameService's shutdown method to shutdown Luna, it could possibly cause players to lose data, etc. if they are in minigames or trades among other things. This should be fixed when the logout queue is properly implemented.
Superclass of all interfaces, contains functions that will be invoked by InterfaceSet when necessary.
An enumerated type describing each type of interface.
A standard interface; closes upon movement and action initialization (ie. smithing, skill guides).
Similar to STATIC, but overlays the inventory with an interface as well (ie. banking, trading).
An interface that doesn't close upon movement and action initialization. These types of interfaces usually do not take up big portions of the screen (ie. wilderness skull and level, tutorial island progress).
An interface displayed on the chatbox. May or may not close on movement and action initialization (ie. cooking dialogue, npc/player dialogues).
An interface displayed on a sidebar tab (ie. ancient spellbook, music player).
Manages the currently open interface.
Plugins are interpreted on startup and wrapped inside EventListener
s, which are then added to EventListenerPipeline
s. Those pipelines are notified of any Event
s which are then subsequently sent through the pipeline to be intercepted by each listener (unless the event is terminated by one of the listeners before the traversal can complete).
With that said, all that would have to be done would be to discard all pipelines and re-interpret all of the plugins which would then reconstruct the pipelines with the newest plugin code.
Last time I tried it, there seemed to be some sort of classpath issue with the Scala compiler/interpreter. I never really got around to looking into it, but this is a feature that I really want added in the near future.
Create a new constructor for item, with signature: Item(String name, int amount)
which will then use ItemDefinition#getItem(String name)
to set its ID. This will result in less magic numbers.
edit: i.e. in plugins/plugin/player/starter_package.scala
then I can design it to fit my exact needs rather than trying to "cheap hack" the core functionality of the EventBus
Gonna be a PITA, might as well get it over with early
Relatively easy task, shouldn't take long at all.
Instead of there being one `dependencies.toml`` file to handle plugin evaluation order, one should be able to have one per directory. This would allow for less confusion— only having dependency orders for specific pieces of content.
For example, say one has three related scripts in the same folder: items.scala
, give_items.scala
, take_items.scala
And lets say both give_items.scala
and take_items.scala
depend on data within items.scala
. You could then define dependencies.toml
within the folder to ensure items.scala
is interpreted first.
[dependencies]
names = [
"items.scala"
]
Some big changes to would have to be made to plugins and the bootstraps in order to nicely support this
dependencies.toml
should be scrappedI've tried running maven and I've tried javac to compile and both give me build errors. Am I doing something wrong?
I want the main Task system to just be a simple, generic, cycle based task... nothing special, nothing to do with the Player
I don't want Action's to be an extension of Task... that will make the design look inconsistent and hard to follow (being able to schedule Action's within the "TaskManager" ???). Rather than extending Task, imposition should be used where needed
Action's should have a set of "traits" (EnumSet could be used be of it's high performance?) that define how it functions... ex.
Could also have specialized Action's that automatically include common traits like "FixedAction" (bad name) that automatically includes all of the STOP_ON_WALK, DROP, EQUIP, etc. traits...
so all in all, the hierarchy should look something like this
io.luna.game.task
io.luna.game.action
if no payload is specified, let it assume payload = "GenericMessageReader"
As of now, plugins are currently written in a Scala 2.11.8 DSL. Scala 2.12 brought Java 8 compatibility which aside from performance enhancements, allows for Java functional interfaces to be expressed as lambdas within Scala.
This nullifies the need for 'hacky' implicit conversions, which should be removed as this update is applied.
I haven't had a chance to look them over yet since I've added Netty's buffer pooling
For code that has a lot of matching, ie.
on[SomeEvent](1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20) { msg =>
...
}
It should be translated to something like
val setMatch$one = Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20)
on[SomeEvent] { msg =>
if(setMatch$one.contains(msg.getId)) {
...
msg.terminate
}
}
By bootstrap.scala
Is it worth the time to do this? Is it even possible? Until the following questions are answered, this issue will remain experimental
The QuadTree should also use Morton order
A discussion needs to be had about the current implications of continuing to use Scala as the content scripting language. I love Scala, but as it stands right now
So I think that a change of languages might be in order to preserve the longevity of Luna. I've come up with a basic list describing what a language implementation needs in order to be the 'perfect' scripting language for this project
I was thinking of using maybe Lua or Ruby. Anyone have any other suggestions on what would be a good replacement language?
https://github.com/lare96/luna/tree/master/src/main/java/io/luna/game/model/def
using mutable arrays/maps doesn't seem like a good way to go about things
a good solution would be to add a static method to those classes called setDefinitons
, the parsers would build the collections and the setDefinitons
method would create an immutable copy of them. subsequent invocation attempts once the immutable collection has already been created will result in an IllegalStateException being thrown
setDefinitons
should also be synchronized
Blocked by issue #17. I'll have to figure out how the packets work as I haven't looked into it yet.
Also, completing this issue will include support for dropping items.
Will just use a queue to hold the dialogue objects in order, and poll them when the next dialogue is needed. The only problem is that options can get very ugly when you have a complicated dialogue. Can't really think of anything to counter that issue at the moment
Simple fix
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.