Coder Social home page Coder Social logo

blightmud / blightmud Goto Github PK

View Code? Open in Web Editor NEW
209.0 209.0 58.0 19.3 MB

A terminal mud client written in Rust

License: GNU General Public License v3.0

Rust 85.60% Lua 13.63% Shell 0.36% Nix 0.42%
hacktoberfest linux lua mac mud mud-client rust rust-lang terminal terminal-based vt102

blightmud's Introduction

Rust GitHub commits since latest release (by SemVer) GitHub issues by-label dependency status Security audit Coverage Status Discord

Blightmud : A mud client for the terminal

Blightmud has been a passion project of mine for some time. A big user of the old but great tinyfugue I always wanted to create my own similar mud client. Even though I don't play much muds these days.

The name?

The client is written in rust. Some navigating throught the thesaurus brought me to the word blight and here we are.

Features

  • Completely terminal based (mac and linux)
  • Telnet:
    • TLS
    • GMCP
    • MSDP
    • MCCP2 (compress2)
    • NAWS
    • TTYPE
    • TELNET CHARSET
    • MSSP
  • Lua scripting:
    • Output and sending
    • Aliases
    • Triggers
    • Timers
    • Customizing status bar
    • Persistent storage
    • Session storage
    • Keybindings
    • Audio playback (music/ambiance and sound effects)
    • Text-To-Speech
    • Mouse scrolling
    • Plugins
    • Sockets (TCP connecting and sending only)
    • Spellchecking
  • Low resource and fast
  • In client help and manuals
  • Native Text-To-Speech functionality (optional compile)
  • Text searching
  • Tab completion
  • Split view when scrolling
  • Screen reader friendly mode

Demo

screenshot

Installation

  • Ubuntu/Debian : Deb packages can be found on the releases page
  • Archlinux/Manjaro : Packages are available on AUR
  • NixOS/Nix : Packages are available in NixPkgs, or from our Flake
  • Mac/Homebrew : We have a homebrew tap brew tap Blightmud/blightmud (intel only, if you're on Apple Silicon (darwin) compiling is the best option)
  • Cargo : If you have rust installed just run cargo install --git https://github.com/blightmud/blightmud blightmud from your favourite terminal.
  • Other/Alternative : Download source and run cargo install --path . from the project root
  • Windows : No native windows support but Blightmud runs fine under WSL

Compiling

  • Install rust
  • Run cargo build to compile
  • Run cargo run to run

Dependencies include, openssl, alsa-libs and pkg-config

  • Ubuntu apt install pkg-config libopenssl-dev libasound2-dev libclang-dev
  • Arch pacman -S pkgconf alsa-lib openssl clang

Compile with text-to-speech

  • Install rust
  • Run cargo build --all-features to compile
  • Run cargo run --all-features to run

In order for this to build correctly you will need to install some additional dev dependencies: libclang and libspeechd. Below are some installation commands that might fit your system:

  • Ubuntu apt install libclang-dev libspeechd-dev speech-dispatcher speech-dispatcher-espeak espeak
  • Arch pacman -S speech-dispatcher espeak

Compile without spellchecking

Some users may encounter issues building the spellcheck feature on MacOS ARM64 (M1/M2). To build Blightmud without the spellcheck feature, use --no-default-features. E.g.:

  • Install rust
  • Run cargo build --no-default-features or cargo build --no-default-features --features text-to-speech
  • Run cargo run --no-default-features or cargo run --no-default-features --features text-to-speech to run

Nix

If you're using Nix or NixOS you can try Blightmud right away with:

  • nix run github:blightmud/blightmud

For developers, once you've cloned this repo you can use the Blightmud flake directly, or with direnv, to have a ready-to-go and self-contained build environment:

  • Run nix build to build Blightmud without text to speech.
  • Run nix build .#blightmud-tts to build Blightmud with text to speech.
  • Run nix develop to enter a dev. env. with Rust nightly.
  • Run nix develop .#stable to enter a dev. env. with the latest stable Rust.

Support, questions and help

Join our discord post an issue or post a question in discussions

Contributing

All contributions are welcome. Check out contributing guidelines.

Side notes

This is my first rust project that has actually grown a bit. Some things might look silly but thanks to rust they should still be safe. Anywho. If you find some antipattern where you have a better idea I'm more then happy to se the PR and learn some more rustier ways.

blightmud's People

Contributors

beldrew avatar cmetz avatar cpu avatar dependabot[bot] avatar hjozwiak avatar lhandf avatar liquidityc avatar lisdude avatar macjabeth avatar robg204 avatar singularpoint avatar sullivant avatar tarkah avatar thequinbox avatar x1a0 avatar xandaros avatar xvxx 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

blightmud's Issues

Bad crash handling and logging

Describe the bug
There are way to many unwraps in the codebase

Solution
Go through code starting at main. Essentially all methods that are called from the main loop main.rs:run() should return a Result<(), Box<dyn std::error::Error>>. Then inside those methods we can replace almost all unwraps with ?.
This should let the software crash nicer and print the problem out on the main buffer and not the alternate on that blightmud operates in.

Enable TELNET TERMINAL-TYPE option (24)

Some muds request TTY negotatiation upon connect.

More often then not these are the muds that mess up the layout of the client. I assume it's by sending ESC[0j (clear).

In an attempt to avoid muds messing with our terminal we should implement terminal type negotation according to: https://tools.ietf.org/html/rfc1091

It should be more or less straight forward to deal with through the libtelnet-rs.

The type we should send is probably "UNKNOWN". Not sure it will solve the issue. But it might.

Mac homebrew package

I'd like to make blightmud available through homebrew. However I don't have a mac so even if I did figure out what's needed I wouldn't be able to confirm and test. If anyone has experience with this stuff I'd love some help.

Split scripting.md into multiple helpfiles.

The helpfile resources/help/scripting.md is really long and bulky. It would be nice if we split this up into several smaller helpfiles and had the main scripting.md point the user to other helpfiles instead.

It's a pretty straight forward task and it can easily be done in increments.

This is the workflow:

  • Check scripting.md for something to break out.
  • Create a new file with an appropriate name.
  • Tell the user about the new helpfile in scripting.md
  • Add the new file to src/ui/helper_handler.rs so that it can be viewed

Eg. lua_timers.md, lua_triggers.md etc.

Tab completion

What the titles says. Tab completion would be great. We could probably generate an options list based on the last 1000 unique words from the server and the last 100 words input at the prompt and some additional default ones for the commands that exist.

Add setting to print received GMCP data

Add a setting that toggles printing of received GMCP data.
This will help a lot when developing scripts for certain muds.

Example:

/set echo_gmcp on/off

Received GMCP data should be printed to screen as well as being sent to script
engine

Block cursor in the input line sticks to the far right when input overflows.

Describe the bug
When you type a long line of input and it overflows to the right, the line will clear and the block cursor will stick to the far right, giving you no indicator of where you're typing.

To Reproduce
Hold down any key until the input line clears and starts from the beginning.

What terminal are you using?
Kitty

Screenshots
Screen Shot 2020-05-25 at 2 19 42 PM

Single characters don't get displayed.

Describe the bug
Individual characters followed by a new line don't get displayed at all.

To Reproduce
Display a sequence of single letters, one per line. I've added the 'single' command to my Blightmud test server to easily test this. Connect to lisdude.com port 1180 and type 'single'. It should display "string test" with each letter appearing on a single line, but in reality it displays nothing.

What terminal are you using?
Kitty, also tested on macOS Terminal

What mud were you playing?
lisdude.com port 1180

Input line editing shortcuts.

With long commands, it would be nice to have an easy way to get around the input line. Here are my proposed changes:

  • Home
    • Output view / scrolling mode: Move to the top of the output view.
    • Input mode: Move to the beginning of the input line.
  • End
    • Output view: Stays the same. Move to the bottom of the output view.
    • Input mode: Move to the end of the line.
  • Alt-Backspace
    • Input mode: Clear the entire input line.
  • Ctrl-Backspace
    • Delete one word back
  • Alt-left or Ctrl-left
    • Input mode: Move back a word.
  • Alt-right or Ctrl-right
    • Input mode: Move forward a word.

Short of full Vim or Emacs emulation, I think these would be good! Even better if it's customizable.

Additional Lua commands

blight:terminal_width()
Return the width of the user's terminal.
(why? MOOs have a setting for maximum line length. There's an MCP package that will automatically set your linewidth for you, but we need to know the width of the terminal to do it.)

blight:version([1 || 2])
No arguments: Return the name and version of the client. e.g. "Blightmud 0.1.0"
With argument 1: Return only the name of the client.
With argument 2: Return only the numeric version.
(why? The MCP client package reports the client name and version to the MOO. Always nice to advertise your favorite client.)

blight:remove_trigger(regex)
Unregister a previously registered trigger.
(why? Part of the MOO MCP specification calls for package negotiation. Once it's done, though, it should be done forever. Removing the trigger would be nice to make sure it's done for good. I realize I could accomplish basically the same thing with a flag, so this is kind of a 'nice to have' thing more than a necessity.)

blight:send(str[, gag])
Add an optional argument to send to gag the line from appearing in local echo.

Sometimes the [255, 249] ([IAC, GA]) falls through to printing

From log:
[01:24:46.646] (7fd9bbbff340) ERROR Unparsable bytes: [27, 91, 48, 59, 51, 55, 109, 27, 91, 51, 50, 109, 72, 58, 53, 55, 50, 48, 32, 27, 91, 51, 55, 109, 27, 91, 51, 50, 109, 77, 58, 51, 56, 52, 56, 32, 27, 91, 51, 55, 109, 77, 97, 100, 58, 48, 37, 32, 27, 91, 51, 49, 109, 66, 108, 58, 32, 48, 27, 91, 51, 55, 109, 32, 88, 80, 58, 49, 54, 37, 32, 91, 100, 98, 32, 101, 98, 32, 108, 114, 93, 255, 249]

Error running release build

Describe the bug
After building a release target of BlightMud, it immediately crashes. This is a brand new Ubuntu 18.04 LTS VM. It works fine with a debug target.

To Reproduce
I would assume that a brand new vm would have this issue, but perhaps there's something peculiar about my environment.

What terminal are you using?
Originally, openssh running in a tmux pane to another server. I then also tried using putty to connect directly.

What mud were you playing?
Host? 3kingdoms Port? 23 Website? http://www.3k.org (not mud specific though, I haven't connected yet)

Screenshots
If applicable, add screenshots to help explain your problem.
image

So also have seen two error reports.
1st:

name = 'blightmud'
operating_system = 'unix:Ubuntu'
crate_version = '0.4.2'
explanation = '''
Panic occurred in file 'src/main.rs' at line 160
'''
cause = 'called `Result::unwrap()` on an `Err` value: Os { code: 25, kind: Other, message: "Inappropriate ioctl for device" }'
method = 'Panic'
backtrace = '''

   0: 0x56427bce2a43 - std::rt::lang_start::{{closure}}::h799748cf7108c9ba
   1: 0x56427bfebe78 - std::rt::lang_start_internal::{{closure}}::h44dc84346eeba462
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/rt.rs:52
                 - std::panicking::try::do_call::h3559ee6be71549e2
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/panicking.rs:331
                 - std::panicking::try::h78f4c77016541848
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/panicking.rs:274
                 - std::panic::catch_unwind::hfe46f382761b3d41
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/panic.rs:394
                 - std::rt::lang_start_internal::he05790f0cb2000df
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/rt.rs:51
   2: 0x56427bc8e7b2 - main
   3: 0x7f8e5d414b97 - __libc_start_main
   4: 0x56427bc6131a - _start
   5:        0x0 - <unresolved>'''

And 2nd:

name = 'blightmud'
operating_system = 'unix:Ubuntu'
crate_version = '0.4.2'
explanation = '''
Panic occurred in file 'src/main.rs' at line 172
'''
cause = '[!!] Panic: No such file or directory (os error 2)'
method = 'Panic'
backtrace = '''

   0: 0x55650a587e78 - std::rt::lang_start_internal::{{closure}}::h44dc84346eeba462
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/rt.rs:52
                 - std::panicking::try::do_call::h3559ee6be71549e2
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/panicking.rs:331
                 - std::panicking::try::h78f4c77016541848
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/panicking.rs:274
                 - std::panic::catch_unwind::hfe46f382761b3d41
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/panic.rs:394
                 - std::rt::lang_start_internal::he05790f0cb2000df
                at /rustc/c7087fe00d2ba919df1d813c040a5d47e43b0fe7/src/libstd/rt.rs:51
   1: 0x55650a22a7b2 - main
   2: 0x7f1df4796b97 - __libc_start_main
   3: 0x55650a1fd31a - _start
   4:        0x0 - <unresolved>'''

A Lua method to display text along the bottom bar.

As the title says, a way to display your own text in the bottom bar (where (more) shows up while scrolling). This allows for plugins to display and update persistent custom information.

I don't think we need anything fancy, function-wise:

blight:output_to_bottom_bar(str)

Prints persistent output to the bottom bar. Space is reserved at the front for (more) when scrolling.
Eg. blight:output_to_bottom_bar("Assists: 0 Memory: 1.51 GB Players: 63")

blight:clear_bottom_bar()
Clears all text from the bottom bar. "(more)" is unaffected.

Clear is redundant, as you could just output a blank string, but I think it's nice for clarity of what you're intending to do.

Save connection details

It'd be nice to be able to save the connection details to a server as a friendly name, that can then be used with /connect to easily reconnect to that server. And the ability to list all saved connections. Or maybe an alternative would be to have a "Recently Connected" list?

Would be interested in your preference.

aggressively keeping the telnet session alive

Last request I have, honest (and everything else works amazing to date):

Users who may have their client live in Azure will have an issue where any period of inactivity will lead to their session timing out in a weird way. The client will remain responsive, but the mud will stop responding. This is because of azure's session firewalls.

In tintin, we get around this by creating a timer (tick) that sends a keepalive (without unidling your character on the mud), with something like this:

#tick {keepalive} {#send \xFF\xF1} {60};

These hex characters seem to be unliked by rlua as they come back into UTF8, but honestly, this is a bad way of fixing this anyway. Can we have a world option to enable / set interval of keepalives on the telnet session? ie: setKeepalive on the TcpStream?

Scroll output using scroll wheel

Scroll wheel has differen effects in different terminals. A common one seems to be "up/down arrow". I think termion is able to grab scroll wheel.

Ignore duplicate commands in a sequence.

Instead of every line of input being saved, it would be nice if it checked against the last saved input. If they're the same, don't bother saving it again. This way if you have a lot of lines of the same input, you only have to hit the up arrow twice to get to the command before that instead of 30 times.

e.g.

>get pancakes
>attack clown
>attack clown
>attack clown
>attack clown
>eat pancakes
>

In this scenario, you have to hit the up arrow six times to get back to "get pancakes". With this suggestion, it would only save "attack clown" once. So your command history would look like this:

>get pancakes
>attack clown
>eat pancakes

A mere three up arrows away!

Mud logging

Not the regular system log that exists already. Actual logging of the mud play. Shouldn't be too hard to get working. All mud output is stripped of escape chars before testing the line for triggers. Should probably save the line in the log at this point as well.

Bad prompt handling

Ok. So I’ve only been testing this client on muds that send the IAC GA opt at end of transmissions. This definitely doesn’t fly with all muds. The result being that the prompt doesn’t render.

I’ve got a solution. It’s on top priority.

Line wrapping problem

Received this on reddit:

wrapping_bug

Seems the wrapping sometimes adds a blank line between wrapping lines.

Blightmud crashes on startup

Describe the bug
For a moment, the UI blinks and tries to take over the terminal, then crashes.

To Reproduce
Run it on MacOS Catalina 10.15.4 with zsh with Oh My Zsh installed, rustc 1.43.1 (8d69840ab 2020-05-04)
log.txt

What terminal are you using?
MacOS Terminal with zsh and Oh My Zsh installed.

Calling reset() with a running timer crashes.

Describe the bug
If you call the reset() function from Lua after a timer has been added, the client will crash the next time the timer runs.

To Reproduce

  1. crashtest.lua with the content: blight:add_timer(10, 0, function() blight:output("hi"); end);
  2. reset.lua with the content: blight:reset();
  3. Connect somewhere
  4. /load crashtest.lua
  5. /load reset.lua

In about 8 seconds, the client will crash.

What terminal are you using?
Kitty

Scrollback "sticks" with very long lines.

When you have a single line that encompasses several screens of scrolling, the top half or so of the line will stick to the top while the bottom scrolls. Eventually it will catch up and scroll properly. Additionally, it seems the top part of the line will disappear entirely when you scroll back to the beginning. The line is question is 31,542 characters long.

scrolling

I hope this terrible gif helps demonstrate what I mean.

Reading from the output buffer from Lua.

I've implemented the arguably more complex MCP 2.1 protocol in Lua just fine. However, a lot of old MOOs don't support MCP 2.1, so I was adding support for the original MCP 1.0 simpleedit, which looks like this:

#$# edit name: Wizard:some_verb upload: @program #2:some_verb
{x, y, z} = args;
player:tell("This is some code.");
return x * y * z;
.

It's easy enough to set up a trigger to capture the title and upload command from the first line. Where I'm having trouble, however, is catching the verb code. Anything between the #$# line and the single period at the end is verb code that needs to be captured.

I'm not sure how to do that, or even what suggestion to make to catch that code. The best I could come up with is creating a catch-all trigger in the first callback (which seems kind of gross) until it gets the single period, but then I can't remove the trigger. I could have a flag that dictates whether or not I'm interested in each line, but having every single line from the server get caught and pass through the callback seems inefficient.

Any ideas?

Crash when backspacing at the beginning of the input area.

Describe the bug
The client crashes when backspacing at the beginning of the input line. Thread '' panicked at 'attempt to subtract with overflow', src/command.rs:103:32.

To Reproduce

  1. Type anything into the input area.
  2. Move the cursor to the beginning of the line.
  3. Backspace.

What terminal are you using?
Kitty

Output view navigation.

When scrolling through output view, a couple of things would be really nice:

  • Search
    • / - Begin search
    • n - Next result (searching downward)
    • p - Previous result (searching upward)
    • N - Last result
    • P - First result
  • Jump to previously echoed commands
    • Alt-Spacebar - Jump to the last command you typed in the output view. Press again will go back to the command before that, and so on.

Parts of output are missing.

Describe the bug
Sometimes bits of output aren't displayed. This is possibly when data is in tables with long spaces? Not sure.

To Reproduce

  1. /connect lisdude.com 1180
  2. longline2

Repeat the longline2 command several times. Notice that there are gaps in different places each time.

What terminal are you using?
Kitty + tmux

What mud were you playing?
lisdude.com:1180

Screenshots
Screen Shot 2020-05-25 at 10 27 07 AM

Freeze when hitting up arrow before connecting.

If you hit the up arrow on the welcome screen, the input bar will say "thread '' panicked at 'Out of bounds access' etc etc. From here there doesn't seem to be any way to interact with the client again.

Navigating the input string

Currently you can type and hit backspace when inputting commands at the prompt.
It would also be nice to be able to step back and forth in the input to edit mistakes or whatever.

I guess the obvious solution is to keep track of the cursor index in command.rs. Include the index when drawing to the screen etc. This should also remedy the issue that makes the cursor "jump around" when output is being printed from the server.

Timer output won't display until there is other server activity.

Describe the bug
When a timer is configured to use blight:output() every second, the text isn't displayed until other text comes in from the MUD. It seems to be indefinitely queued.

To Reproduce

  1. Create a script.lua file with contents: blight:add_timer(1, 0, function() blight:output("hi"); end);
  2. /load script.lua
  3. Connect to a quiet server (lisdude.com:1180 is my test environment)
  4. Do nothing.

As you do nothing, you'll notice no 'hi' appearing. But if you hit enter, suddenly all of the hi's will appear.

What terminal are you using?
Kitty

What mud were you playing?
lisdude.com port 1180

Trigger output not rendering after trigger line

Since we use a queue for terminal output. Queueing output from a trigger means it sometimes doesn’t show where the triggering line appeared in the output.

We should be able to get around this by returning output from a trigger as an alternative for using ‘blight:output()’.

Som trial and error might be needed here to create a comfortable solution.

Crash when trying to display a single long line with no spaces.

When you display a single string of characters with no spaces, the client will unceremoniously crash. This seems to happen around 1,000 or more characters.

If you're a MOO guy, you can reproduce it with this eval:
;;str = ""; chars = strsub($string_utils.ascii, " ", ""); for x in [1..2000] str = tostr(str, chars[random($)]); $command_utils:suspend_if_needed(0); endfor player:tell(str);

Use saved name for log file directory

Rather than placing log files in a directory named after the server address, could they be put into the saved server name instead? Then it can fall back to naming the directory after the address if it's not a saved server.

So, for instance, we have:

Saved servers:

  • Name: albori, Host: toastsoft.net, Port: 1234
  • Name: will, Host: toastsoft.net, Port: 1234

That would create two directories in /blightmud/logs: albori and will. And if I manually /connect toastsoft.net 1234 it would then fall back to /blightmud/logs/toastsoft.net.

This comes in handy when you have multiple characters on the same MUD. I regularly connect to at least two (my administrator and my player character) and it would be nice to differentiate the log files for each one.

Autoload a script when connecting to a saved server.

Auto-loading a script after connecting would be great for:

  • MOOs auto-loading the MCP package.
  • Connecting with a saved username / password.
  • Auto-loading all of that server's scripts and triggers and aliases and timers and whatnot's without having to remember to /load every time.

Scroll origin locking

Scrolling sometimes seems to get stuck at a certain point. Scrolling up will scroll from this new origin point and scrolling back down to the end only brings you back to this point. Obscuring everything that happened after

Reconnect command.

/reconnect, /rc

If the current connection is dead, reconnect to the last server. If the connection is active, disconnect and then reconnect to the current server.

Specify a server as a command line argument

Without tabs, we'll have to use tmux or screen to open multiple sessions at once. To make this easier, it would be great if Blightmud let you specify a saved server or hostname / port on the command line. This way we can write a script to connect to all of our MOOs and MUDs at once instead of manually connecting one at a time.

Add markdown support

Helpfiles are currently writte in MD. This makes them look good in github but not so much when viewed in the client.

Using this lib: https://github.com/netvl/md.rs we could perhaps improve the look of the markdown files when shown in the client.

Coloring is out, but the other data should turn out better then what we have at least.

Sending GMCP from lua

Currently one can register to modules and listen for updates for these modules.
Lastly the user should be able to send GMCP strings to the server from lua.

Should be pretty easy to get done. Start looking at user_data.rs and see how the related methods work.

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.