Code Monkey home page Code Monkey logo

rbxutil's Introduction

CI Docs

RbxUtil

Module Dependency Description
BufferUtil BufferUtil = "sleitnick/[email protected]" Buffer utilities
Comm Comm = "sleitnick/[email protected]" Comm library for remote communication
Component Component = "sleitnick/[email protected]" Component class
Concur Concur = "sleitnick/[email protected]" Concurrent task handler
EnumList EnumList = "sleitnick/[email protected]" Enum List class
Input Input = "sleitnick/[email protected]" Basic input classes
Loader Loader = "sleitnick/[email protected]" Requires all modules within a given instance
Log Log = "sleitnick/[email protected]" Log class for logging to PlayFab
Net Net = "sleitnick/[email protected]" Static networking module
Option Option = "sleitnick/[email protected]" Represent optional values in Lua
PID PID = "sleitnick/[email protected]" PID Controller class
Quaternion Quaternion = "sleitnick/[email protected]" Quaternion class
Sequent Sequent = "sleitnick/[email protected]" Sequent class
Ser Ser = "sleitnick/[email protected]" Ser class for serialization and deserialization
Shake Shake = "sleitnick/[email protected]" Shake class for making things shake
Signal Signal = "sleitnick/[email protected]" Signal class
Silo Silo = "sleitnick/[email protected]" State container class
Streamable Streamable = "sleitnick/[email protected]" Streamable class and StreamableUtil
Symbol Symbol = "sleitnick/[email protected]" Symbol
TableUtil TableUtil = "sleitnick/[email protected]" Table utility functions
TaskQueue TaskQueue = "sleitnick/[email protected]" Batches tasks that occur on the same execution step
Timer Timer = "sleitnick/[email protected]" Timer class
Tree Tree = "sleitnick/[email protected]" Utility functions for accessing instances in the game hierarchy
Trove Trove = "sleitnick/[email protected]" Trove class for tracking and cleaning up objects
TypedRemote TypedRemote = "sleitnick/[email protected]" Simple networking package for typed RemoteEvents and RemoteFunctions
WaitFor WaitFor = "sleitnick/[email protected]" WaitFor class for awaiting instances

rbxutil's People

Contributors

4812571 avatar ambergamefam avatar christopher-buss avatar damondriscoll avatar devsarim avatar eqicness avatar grilme99 avatar guidable0 avatar howmanysmall avatar jisham318 avatar joshuaclarkelua avatar kravu0 avatar luarook avatar malakai0 avatar pakila0 avatar rbxtyler avatar rodrick160 avatar sigmathetatech avatar sleitnick avatar slxughtxr 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  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  avatar  avatar  avatar

rbxutil's Issues

Add waitForInstance to Component

Currently, I find myself doing repeat task.wait() until Component.FromInstance() very often. It would be nice if we had a Component.WaitForInstance method that returned a promise.

An example: Component.WaitForInstance(instance, timeout):andThen(function (component) end)

Settings RenderPriority causes RenderSteppedUpdate calls to continue beyond lifetime on component

If you set the RenderPriority of a component, and have more than one component of the same type active at once, then removing the component will not reliable unregister the callback.

This, I believe is caused by _renderName being stored on self in StartComponent rather than component. This results in there being cases where two components can end up with the same _renderName which causes all but one of them not to unregister correctly when the component stops.

Gamepad Input

Currently, the Input Util does not contain any support for the gamepad input type. It would be useful to have this included so that we can more easily build cross-platform games without needing to use the native UserInputService

Add examples to the documentation

At the moment, it is difficult to learn a new util module because there are no examples showing us how to use them.

To solve this problem, it would be nice if every util module had a quick example of the main functionality

Combined Silos do not remove keys correctly.

When dispatching SiloUtil.None in a combined silo, it does not remove the value from the state.

Single Silo:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Silo = require(ReplicatedStorage.Packages.Silo)
local SiloUtil = require(ReplicatedStorage.Packages._Index["[email protected]"].silo.Util)

local initialState = {
	myValue = 123,
}

local mySilo = Silo.new(initialState, {
	Set = function(state, action)
		state.myValue = action.value
	end,
})

mySilo:Watch(function(state)
	return state
end, function(objects)
	print(objects)
end)

task.wait(1)

print("Set Empty")
mySilo:Dispatch(mySilo.Actions.Set({
	value = SiloUtil.None
}))

Final Output: {}


Combined Silo:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Silo = require(ReplicatedStorage.Packages.Silo)
local SiloUtil = require(ReplicatedStorage.Packages._Index["[email protected]"].silo.Util)

local initialState = {
	myValue = 123,
}

local mySilo = Silo.new(initialState, {
	Set = function(state, action)
		state.myValue = action.value
	end,
})

local MAIN = Silo.combine({
	mySilo = mySilo,
})

MAIN:Watch(function(state)
	return state.mySilo
end, function(objects)
	print(objects)
end)

task.wait(1)

print("Set Empty")
MAIN:Dispatch(mySilo.Actions.Set({
	value = SiloUtil.None
}))

Final Output: {myValue = 123}
Expected output: {}

Components destroyed during Start() don't properly cleanup

If you have a component that is destroyed before Start() finishes, Stop() is never called and the Heartbeat and other update functions are never unregistered.

This destruction can happen either by the object being destroyed, the tag being removed or the object being removed from the set of ancestors defined.

This requires Start to not immediately return.

The following code will trigger this bug in a fairly easy fashion, but there are a number of other ways this can happen - we've seen it where Start() is waiting for a Knit service to be available before continuing and having the object destroyed before it is.

function SomeComponent:Start()
    self.Instance:Destroy()
    RunService.Heartbeat:Wait()
end

I don't think it's unreasonable to say that Start should return immediately - but this should be documented if this is the case - and ideally detected if it isn't. Often this can be quite an invisible problem and can cause memory leaks. We only noticed this due to a fairly specific set of circumstances but I suspect it may occur invisibly for other users.

Alternatively, TryDeconstructComponent should be revised - perhaps it can wait for Start to complete - i.e. the system could track that a component is in the process of starting. This seems like the least-worst solution to this issue.

Adding threads to Trove can cause errors if thread is of running or normal status

Troves accept threads that will be closed when the trove is cleaned up. However, if this thread is the currently-running thread or marked as the "normal" thread, then it will throw an error. Here are a couple repros:

trove:Add(coroutine.running())
trove:Clean()
-- Error: cannot close running coroutine
trove:Add(coroutine.running())
task.spawn(function()
	trove:Clean()
end)
-- Error: cannot close normal coroutine

A thread that is in the "running" state means that it is the currently-active coroutine. A thread that is in the "normal" state means that it has resumed another coroutine and is awaiting that coroutine to yield back.

Thus, another example of the "normal coroutine" error would be such:

local thread1, thread2

thread1 = coroutine.create(function()
	trove:Clean()
end)

thread2 = coroutine.create(function()
	trove:Add(thread2)
	task.spawn(thread1)
end)

task.spawn(thread2)
-- Error: cannot close normal coroutine

And thus comes the question: How should Trove handle this? Should it just have better error messaging? Ideally, developers should only be placing threads into a Trove that they know will be fine to be closed. Simply checking the thread status on Add won't cut it, because that would assume that Clean would be called from the same thread that Add was called, which may not be the case.

Add Extend and Clone methods to Trove

Trove:Extend() will create a new trove and add it to the one it was extended from. Clone will call Clone() on the given instance & add the cloned instance to the trove.

-- EXTEND:
local trove = Trove.new()
local anotherTrove = trove:Extend() -- A new trove which will be added to `trove` (but does not clone over items within the trove)
trove:Destroy() -- Cleans up trove AND anotherTrove

-- CLONE:
local trove = Trove.new()
local p1 = trove:Construct(Instnace.new, "Part")
local p2 = trove:Clone(p1) -- Clones p1 and adds it to the trove

Net Util: Perhaps allow parenting remotes to namespaces

Currently Net RemoteEvent and RemoteFunction methods parent the created instances to the Net module, that's fine. But I found my self having a bunch of modules with identical function names, which is convenient for prototypes and non-professional dev projects (like the one I'm hired to develop).

-- Server
local remoteEvent = Net:RemoteEvent("PointsChanged", Namespace)


-- Client
Net:Connect("PointsChanged", Namespace, function(points)
	print("Points", points)
end)

if no namespace is provided when creating the remote it will just default to the current behavior, parenting to the Net module.

and when calling Connect/Invoke it would search in the provided namespace, if it's not provided then it'' either just do a shallow search within the module (or make it recursive search? Not sure how good that idea is)

Not exactly sure if this is a "good idea" but I found it rather useful for my current situation. Thanks a lot for the module! COMM was just overkill for my use case so this is very appreciated โค๏ธ

Implement `PreferredInput.InputType` as enum-list

Currently, the PreferredInput module exports a type for input types, i.e InputType. I think that in addition, It should export an InputType enum list, so I can use it effectively e.g, PreferredInput.InputType.Keyboard. Currently, I've to use direct strings (the type-auto complete helps) but it'd be better if instead the type could be removed in favour of a dictionary of input types exposed to the developer.

RbxUtil Needs a Tween Util

RbxUtil lacks any sort of Tween Util. In making a Tween Util, it'd be reassuring for many developers who rely on the utils provided by RbxUtil.

How to use Trove to cleanup promises

Hi Sleitnick, I learned about the Knit framework with your Knit Tutorial video, and I appreciate you writing the framework and sharing this great tutorial! I learned a lot from it.

I have a question. Now that using Trove, how can I cancel the delay event of a promise ?(about respawn the ball๏ผ‰I tried to add a dealy promise to Trove, but I was reminded that there was no cleanup function. I can record this promise into a variable and execute its cancel event when the script is destroyed. I wonder if it can be implemented in combination with Trove or something else

Thanks

Component.Auto not found in V2

HI Sleitnick, I start using the knit framework and watch your very good "Knit Game Tutorial".
In the video the util Component V1 ha an Auto method, but in V2 I don't find it.
I create a Goal Component in this way:

local Goal = Component.new({
  Tag = "Goal"
})

function Goal:Construct()
  self._maid = Maid.new()
  print("Goal created")
  self._maid:GiveTask(function()
    print("Goal destroyed")
  end)
end

function Goal:Stop()
  self._maid:Destroy()
end

return Goal

The Goal component is correctly attached to the Goal instances, the problem is that when I add at runtime a new Goal instance with tag "Goal", the component is not automatically attached.

I discovered that if I create the instance on server the component is attached, on the client no. I think this is the right behaviour.

Investigate failing CI unit test

There's a problem with the unit test CI process. For now, unit tests should be run locally before merging any changes. This issue seems to be somehow linked to the run-in-roblox tool. Still need to investigate.

Bug with components

I've discovered some weird behavior with components that I believe is a bug.

Let's say we have a component called "thing".
The construct method of the component includes a task.wait(1) (or some other synchronous function).
If we clone a part with that tag from replicated storage into workspace the construct method of "thing" will run twice.

I've uploaded an example here:
https://github.com/Vilbat/ComponentBug

Edit: The construct method also runs twice if the ShouldConstruct() method yields.

Trove should support threads

With the addition of coroutine.close, it would be nice if Troves could have threads added to them. When the trove is cleaned up, coroutine.close will be called against the given thread.

local co = coroutine.create(function()
    -- ...
end)
trove:Add(co)
trove:Clean() -- Ends up calling coroutine.close(co)

WaitForInstance waits indefinitely

As far as I'm aware, and please correct me if I'm wrong, but the promise fromEvent created by WaitForInstance if the component class does not currently exist on the object will exist forever if the component class will never actually exist on the object.

A propsal for this could be to add an optional timeout (though the use of Promise:timeout(), which would add safety to the component as the event will resolve itself if the instance doesn't exist after a certain timeframe.

Another propsal could just be to issue a warning, much like the standard Roblox WaitForChild, that would let the developer know that there is a possible infinite wait on the obejct, so that the developer is at least warned that the class does not appear in a timely manor.

Cannot access modules like `PreferredInput` in the new version of Input (2.0.0)

Input recently got a new update where it changes how you should access it's modules. After installing the latest version of Input (2.0.0), I cannot access it's modules like the way it is stated in the documentation to do so, e.g local PreferredInput = require(packages.Input.PreferredInput) errors.

This is because of how Wally structures installed packages. Since in the wally packages folder where all my installed packages are, the source code of the Input package module is as follows:

return require(script.Parent._Index["[email protected]"]["input"])

And since the Input module it self returns a nil value, I can't access the modules and I'm forced to use an older version of Input i.e 1.3.3..

Add Observe to PreferredInput

Right now, if you want to observe the preferred input, you have to write two lines: one to connect to the change event, and one to fire immediately. It would be nice if there was an Observe method that would do both.

local Input = require(somewhere.Packages.Input)
local PreferredInput = Input.PreferredInput

local connection = PreferredInput.Observe(function(preferred) ... end)

Signal `ScriptConnection` should contain public `Connected` member

It would be useful to have Connected as a member of signal connections.

Right now it's a private member, _connected.

One thing to note is that it's not just renaming it, but also you would need to change :DisconnectAll to this:

function Signal:DisconnectAll()
        local item = self._handlerListHead
        while item do
            item.Connected = false

            item = item._next
        end
	self._handlerListHead = false
end

To make sure it's consistent with RBXScriptSignal behaviour.

I would open a PR but there's already for one Signal and I'm scared of git collisions that I don't know how to mess with... so...
It's easy enough for you to just change all occurences of _connected to Connected and replace :DisconnectAll with this.

Trove occasionally fails to cleanup at least one object once it cleans a promise

Over the past month I've noticed that after converting old code to use Trove instead of Janitor there were certain scenarios where not all objects in a trove were properly cleaned. It always seemed to involve a promise and I just assumed I was doing something wrong.

However, after a bit of digging tonight, I discovered that Trove occasionally fails to cleanup an object once it cleans a promise added through Trove:AddPromise(). Apparently when a promise is cleaned up, it removes itself from the Trove._objects array, shifting all the proceeding objects, causing the in pairs loop inside Trove:Clean() to skip at least one object if there is one.

It's really easy to reproduce this issue:

local trove = Trove.new()

trove:AddPromise(Promise.delay(5)):finallyCall(print, "Promise cleaned!")
trove:Add(function() print("Fn 1 cleaned!") end)
trove:Add(function() print("Fn 2 cleaned!") end)
trove:Clean()

--> Promise cleaned!
--> Fn 1 cleaned!

TableUtil.Map() documentation issue

TableUtil's documentation shows an example of TableUtil.map() taking in a predicate of form (key, value) -> nil. However, TableUtil'
s code, and documentation states that the predicate is (value, key, tab) -> nil.

The following code from the documentation is incorrect:

local t = {A = 10, B = 20, C = 30}
local t2 = TableUtil.Map(t, function(key, value)
	return value * 2
end)
print(t2) --> {A = 20, B = 40, C = 60}

The code should read:

local t = {A = 10, B = 20, C = 30}
local t2 = TableUtil.Map(t, function(value)
	return value * 2
end)
print(t2) --> {A = 20, B = 40, C = 60

RbxUtil Needs a Zone Util

RbxUtil lacks any sort of Zone Util. In making a Zone Util, it'd be reassuring for many developers who rely on the utils provided by RbxUtil. Perhaps if a player is detected in a zone, it'll run a provided callback like PreferredInput, or the user could connect to a signal. It'd also be nice to have Trove and even Component support in some form. An example of this util being used is different zones giving the player different lighting ambiance, so if a Zone is named blue, then it gives a blue tint to the players screen. A proposed name for this util is Region.

Add `TableUtil.Values()`

Great module! I've been using it for a while, but I'd like see TableUtil.Values(), why not?, if TableUtil.Keys() exists then I guess TableUtil.Values() makes sense

Add a FireFor method for RemoteSignals

It is currently difficult to fire a RemoteSignal for a specific set of players. FireFilter can work for this, but gets clunky in contexts where the players are already grouped in some sort of table set. Adding FireFor could allow developers to pass a table of players to be fired upon.

Signature:

Signal:FireFor(players: {Player}, ...args: any)

Example:

local players = {someListOfPlayers}
signal:FireFor(players, "Hello there!")

Would it be possible to check the length in table-util/Truncate?

Truncate currently uses table.move into a freshly created table:

local function Truncate(tbl: Table, len: number): Table
	return table.move(tbl, 1, len, 1, table.create(len))
end

When len is greater than or equal to the length of the table, this operation redundantly copies the data; more importantly, it does an out-of-range move (with len > #tbl). We're looking into possibly limiting table.move indices to be in range of 1..#table -- while we haven't finished analyzing the production data for the impact of this change, and aren't sure if we actually want to make this change, a large outlier responsible for ~90% of out of range moves is the use of table-util in a top Roblox game, so it would be great to see if this can be changed in table-util.

Component doesn't remove ancestryChangedHandle from the component class' trove when an instance is untagged

Right now there's a minor memory leak with the Component module. When an instance is untagged, it will disconnect the ancestryChangeHandle connection and remove it from watchingInstances, but not from the component class' trove. To fix this, InstanceUntagged should be something along these lines:

local function InstanceUntagged(instance: Instance)
	local watchHandle = watchingInstances[instance]
	watchingInstances[instance] = nil
	
	self[KEY_TROVE]:Remove(watchHandle)
	
	TryDeconstructComponent(instance)
end

You can kill two birds with one stone by using Trove:Remove() to disconnect the connection and to remove its reference from the component class' trove.

Change Trove:AttachToInstance to .Destroying

On March 7th, Roblox released Destroy() replication, and with that came the long awaited .Destroying event of which is a better alternative to .AncestryChanged as .AncestryChanged fires every time the instances ancestry changes and it's often a shot in the dark, whereas .Destroying ensures the instance is deleted.

Janitor (https://github.com/howmanysmall/Janitor) has already made the switch to .Destroying
https://devforum.roblox.com/t/destroy-can-now-replicate-to-clients/1694890

Component Extensions are limited in nature

Component Extensions can only hook into lifecycle events to a component.
While this is nice for creating a component janitor and destroying it when you no longer need the component, it can also restrict what extensions could be. I'm proposing that extensions should be able to hook more deeply into the process. Being able to reject or otherwise prevent component creation would allow for RequiredComponents (from the old V1 module), something which is quite a task with only lifecycle hooks, and more creative ideas.

Silo Dispatching an empty table does not clear state.

Not sure if this is intentional or if there is a different way of doing this. I want to set state.objects to an empty table, but it does not clear the table.
After it prints Set Empty it continues to print a non empty table.
Code:

local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Silo = require(ReplicatedStorage.Packages.Silo)

local initialState = {
	objects = {},
}

local silo = Silo.new(initialState, {
	Set = function(state, action)
		state.objects = action.objects
	end,
})

silo:Watch(function(state)
	return state.objects
end, function(objects)
	print(objects)
end)

print("populating")
for i = 1, 5 do
	silo:Dispatch(silo.Actions.Set({
		objects = {[(tostring(i))] = true}
	}))
	task.wait(0.1)
end

task.wait(1)

print("Set Empty")
silo:Dispatch(silo.Actions.Set({
	objects = {}
}))

Add RemoteProperty to Comm

Add a RemoteProperty to Comm, for both the server and client.

Possible API:

Server:

  • RemoteProperty.new(defaultValue: any)
  • rp:SetAll(value: any)
  • rp:Get(player: Player?): any
  • rp:Set(player: Player, value: any)
  • rp:Clear(player: Player)
  • rp:ClearAll()

Client:

  • ClientRemoteProperty.new()
  • rp:Get(): any
  • rp.Changed: Signal<any>

Add component:GetAll() method

Components should have a GetAll() method to get all components for a given component class.

local MyComponent = Component.new({Tag = "MyComponent"})

...

local componentInstances = MyComponent:GetAll()

Missing add to table in Concur Docs

Currently the Concur documentation has this:

game:BindToClose(function()
    local all = {}
    for _,player in ipairs(Players:GetPlayers()) do
        local save = Concur.spawn(function()
            DoSomethingToSaveData(player)
        end)
    end
    local allConcur = Concur.all(all)
    allConcur:Await()
end)

The all table is never populated, and as such the allConcur:Await() does nothing. A table.insert (or equivalent) should be added.

Inconsistent lifecycles of Components bound to the same Instance with "slow" Construct method

Documentation of the Start method says:

At this point in time, it is safe to grab other components also bound to the same instance.

And it works perfectly when we use multiple components bound to the same Instance. I like this feature, it looks like Knit's Services/Controllers, but with a focus on game entities. All lifecycles are synchronized.

But it is not true if some components yelds the Construct method (for receiving some data for example):

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local Component = require(ReplicatedStorage.Packages.Component)


local Component1 = Component.new({ Tag = "SomeTag" })
local Component2 = Component.new({ Tag = "SomeTag" })
local Component3 = Component.new({ Tag = "SomeTag" })
local Component4 = Component.new({ Tag = "SomeTag" })
local Component5 = Component.new({ Tag = "SomeTag" })

function Component1:Construct()
	wait(0.1)
	print("*** Component1 constructed")
end
function Component1:Start()
	print("*** Component1 started")
	print("Component2:", self:GetComponent(Component2))
	print("Component3:", self:GetComponent(Component3))
	print("Component4:", self:GetComponent(Component4))
	print("Component5:", self:GetComponent(Component5))
end

function Component2:Construct()
	print("*** Component2 constructed")
end
function Component2:Start()
	print("*** Component2 started")
	print("Component1:", self:GetComponent(Component1))
	print("Component3:", self:GetComponent(Component3))
	print("Component4:", self:GetComponent(Component4))
	print("Component5:", self:GetComponent(Component5))
end

function Component3:Construct()
	print("*** Component3 constructed")
end
function Component3:Start()
	print("*** Component3 started")
	print("Component1:", self:GetComponent(Component1))
	print("Component2:", self:GetComponent(Component2))
	print("Component4:", self:GetComponent(Component4))
	print("Component5:", self:GetComponent(Component5))
end

function Component4:Construct()
	print("*** Component4 constructed")
end
function Component4:Start()
	print("*** Component4 started")
	print("Component1:", self:GetComponent(Component1))
	print("Component2:", self:GetComponent(Component2))
	print("Component3:", self:GetComponent(Component3))
	print("Component5:", self:GetComponent(Component5))
end

function Component5:Construct()
	print("*** Component5 constructed")
end
function Component5:Start()
	print("*** Component5 started")
	print("Component1:", self:GetComponent(Component1))
	print("Component2:", self:GetComponent(Component2))
	print("Component3:", self:GetComponent(Component3))
	print("Component4:", self:GetComponent(Component4))
end

CollectionService:AddTag(workspace:GetChildren()[1], "SomeTag")

EnumList should error when trying to index a nil value within the Enums table

I believe this was removed when table.freeze was added, but table.freeze only fulfills what __newindex did. It does not error if you index a nil value.

In my opinion, it is fairly useful for debugging, especially if you are changing around the Enum names around quite a bit. I had to spend 20 mins to debug an issue related to this, and it would be nice if the behavior matched that of the Roblox Enums, which error if you try to index for a non-existent Enum.

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.