Giter VIP home page Giter VIP logo

grapevine's Introduction

Grapevine

Grapevine

Grapevine is a MUD chat network.

WebSocket Protocol

View the websocket details on Grapevine.

Server

Requirements

This is only required to run Grapevine itself, the server. These are not required to connect as a game. See the above websocket docs for connecting as a client.

  • PostgreSQL 11
  • Elixir 1.9.0
  • Erlang 22.0.4
  • node.js 10.13.0
  • Yarn

Setup

cd apps/grapevine
mix deps.get
mix compile
yarn --cwd assets
mix ecto.reset
mix phx.server

This will start a web server on port 4100. You can now load http://localhost:4100/ to view the application.

Running Tests

MIX_ENV=test mix ecto.create
MIX_ENV=test mix ecto.migrate
mix test

Telnet Web Client

Telnet connections live in the apps/telnet application. This node holds the telnet connections so the main application can reboot on deploys and not drop active game connections.

For deployment the telnet application needs to be on its own erlang node. You can connect with something similar to:

config :grapevine,
  topologies: [
    local: [
      strategy: Cluster.Strategy.Epmd,
      config: [
        hosts: [
          :grapevine@localhost,
          :telnet@localhost,
        ]
      ]
    ]
  ]

Docker Compose

To run a production like system locally, you can use docker-compose.

The following commands will get a system running locally at http://grapevine.local. This also assumes you have something listening locally (such as nginx) that will proxy port 80 traffic to port 4100.

docker-compose build
docker-compose up -d postgres
docker-compose up -d socket
docker-compose up -d telnet
docker-compose run --rm web eval "Grapevine.ReleaseTasks.migrate()"
docker-compose run --rm web eval "Grapevine.ReleaseTasks.seed()"
docker-compose up web

Simple nginx config

This nginx config will configure your server to listen for grapevine.local and forward to either a local development server or the docker-compose setup from above.

    upstream grapevine {
            server localhost:4100;
    }

    server {
            listen 80;
            server_name grapevine.local;

            location / {
                    proxy_set_header Host $host;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    proxy_http_version 1.1;
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header Connection "upgrade";
                    proxy_pass http://grapevine;
            }
    }

Setting up a new Play CNAME

  • Game sets the CNAME to client.grapevine.haus
  • Game must have a homepage url
  • Game must have the web client enabled
  • Update game's record for their CNAME
  • Update nginx config for new domain
  • Run certbot for the new domain
  • Refresh CNAMEs in ETS Grapevine.CNAMEs.reload()

Kubernetes

Some notes on installing into kubernetes:

# Set up nginx ingress
helm install nginx-ingress stable/nginx-ingress --set controller.publishService.enabled=true

grapevine's People

Contributors

ahacop avatar carlism avatar dependabot[bot] avatar donaldducky avatar ingwarsw avatar oestrich avatar sb8244 avatar swiftausterity 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

grapevine's Issues

Load a random MUD

Have a "Surprise Me" game picker on the game listing page.

Two modes:

  • Surprise Me: Randomly pick an online game and show the profile
  • Surprise Me & Play: Randomly pick an online game and launch the web client (only enabled games)

Require a `supports` key when authenticating

Should be just supports: ["channels"] for now, but eventually a game can opt into direct messaging. This should be validated against a list on the server and refuse entry for extra values.

Strip MXP tags

On a new message, gossip should strip MXP tags before broadcast.

Achievements

Gossip will soon have achievements, games can unlock them for players and they will be displayed on Grapevine if the character has been associated to a profile.

Why?

To enable discovery of smaller games by letting players collect and share achievements.

Attributes

This is what Gossip will hold on to. Grapevine will handle the character association. This is for a single achievement.

  • ID: generated by Gossip, a UUID used to reference the achievement via events
  • Game: required, game associated with the achievement
  • Title: required
  • Description: required
  • Display: required, default true, if false the achievement is hidden on the public game view
  • Points: required, default 0, min 0, max 100
  • Icon URL: optional, used on grapevine
  • Partial Progress?: required, default false, if true total progress is required
  • Total Progress: optional, see partial progress flag, default 1, min 1, no max

Managing Achievements

Games will manage their achievements on Gossip, from the game show page, similar to connections. Create, edit, update achievements. Possibly delete them, deleting would clear out all associated player unlocks.

On any state change for an achievement, a backbone sync event will occur and update on Grapevine for display. Grapevine will have a public listing on the game profile page.

Games can have a maximum of 500 points in achievements.

Websocket API

Unlock an achievement for a player

To unlock an achievement for one of your players, send this event. The achievement must be from your game's list.

{
  "event": "achievements/unlock",
  "ref": "22d61b78-5ad4-4cfa-933f-02c6172d0964",
  "payload": {
    "achievement": "bbba4676-ac94-4e47-ad6c-f24c0192ac23",
    "unlocked_at": "2018-12-29T13:12:28Z",
    "name": "Player"
  }
}

Response if a ref is provided:

{
  "event": "achievements/unlock",
  "status": "success",
  "ref": "22d61b78-5ad4-4cfa-933f-02c6172d0964"
}

This will echo to the system backbone for Grapevine to catch on and unlock the achievement from there. Grapevine must ack this request before a response is sent back to the game. If Grapevine fails to ack within X seconds then the request is failed and the game is notified.

Record partial progress for a player

To record partial progress for one of your players, send this event. The achievement must be from your game's list.

{
  "event": "achievements/record",
  "ref": "22d61b78-5ad4-4cfa-933f-02c6172d0964",
  "payload": {
    "achievement": "bbba4676-ac94-4e47-ad6c-f24c0192ac23",
    "progress": 10,
    "recorded_at": "2018-12-29T13:12:28Z",
    "name": "Player"
  }
}

Sync

To request the achievements Gossip knows about send this event:

{
  "event": "achievements/sync",
  "ref": "22d61b78-5ad4-4cfa-933f-02c6172d0964"
}
  • ref is required

The response will be a series of sync events back to you:

{
  "event": "achievements/sync",
  "ref": "22d61b78-5ad4-4cfa-933f-02c6172d0964",
  "payload": {
    "total": 45,
    "achievements": []
  }
}

This response is "paginated" by sending chunked up events, correlated by the same ref. Each event will contain up to 10 achievements. The total number of achievements is included in each event so you know how many are being sent.

CRUD Events

To manage achievements inside your game, Gossip can provide a set of CRUD events. refs are required. A response event will be sent back with possible errors.

{
  "event": "achievements/create",
  "ref": "22d61b78-5ad4-4cfa-933f-02c6172d0964",
  "payload": {
    "title": "Achievement"
  }
}
{
  "event": "achievements/update",
  "ref": "22d61b78-5ad4-4cfa-933f-02c6172d0964",
  "payload": {
    "key": "bbba4676-ac94-4e47-ad6c-f24c0192ac23",
    "title": "An Achievement"
  }
}
{
  "event": "achievements/delete",
  "ref": "22d61b78-5ad4-4cfa-933f-02c6172d0964",
  "payload": {
    "key": "bbba4676-ac94-4e47-ad6c-f24c0192ac23"
  }
}

Questions

  • Should we include points? If so, what is the maximum number for a game?
    • If a game is 20 years old, should they be allowed to have "expansion pack" points added to their maximum?
  • How does a game know about the achievements on Gossip?
    • REST API?
    • Websocket sync events similar to the backbone events?
  • Should the game be able to CRUD achievements via websocket events?

Get connected games via the socket

Add a new games support flag to unlock events related to games.

First event would be games/status. To request updates on connected games run:

{
  "event": "games/status",
  "ref": "2134c964-5223-4ea7-99db-c499c13565fe"
}

The connected game would then receive a new event for each connected game. Similar to:

{
  "event":"games/status",
  "ref": "2134c964-5223-4ea7-99db-c499c13565fe",
  "payload":{
    "game": "Olympia",
    "display_name": "Olympia MUD",
    "description": "...",
    "homepage_url": "https://...",
    "user_agent": "ExVenture 0.26.0",
    "user_agent_repo_url": "https://...",
    "connections": [
      {"type": "telnet", "host": "example.com", "port": "4000"},
      {"type": "web", "url": "https://example.com/play"}
    ],
    "supports": ["channels", "players", "tells", "games"],
    "players_online_count": 3,
    "uptime_seconds": 123
  }
}

A single game can also be requested, by short name:

{
  "event": "games/status",
  "ref": "2134c964-5223-4ea7-99db-c499c13565fe",
  "payload": {
    "short_name": "Olympia",
  }
}

Issues with setup

I want to help contribute to the web client, and I guess this is the only way to get it running.

So I followed the instructions laid out. I have all the deps installed.

Under Setup, when I get to yarn --cwd assets, I get:

gesslar@LIAM:~/git/grapevine/apps/grapevine$ yarn --cwd assets
Usage: yarn [options]

yarn: error: no such option: --cwd

I'm not really sure what the above is supposed to do, so I powered on anyway, and tried to run mix ecto.reset and got the following results:

gesslar@LIAM:~/git/grapevine/apps/grapevine$ mix ecto.reset

23:45:53.952 [error] GenServer #PID<0.417.0> terminating
** (RuntimeError) connect raised KeyError exception: key :password not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
    (elixir) lib/keyword.ex:393: Keyword.fetch!/2
    (postgrex) lib/postgrex/protocol.ex:720: Postgrex.Protocol.auth_md5/4
    (postgrex) lib/postgrex/protocol.ex:577: Postgrex.Protocol.handshake/2
    (db_connection) lib/db_connection/connection.ex:69: DBConnection.Connection.connect/2
    (connection) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol
** (Mix) The database for GrapevineData.Repo couldn't be dropped: killed

This all seems like a lot to just work on the client. Am I doing something wrong?

Replay channel messages

Follows up #121

Add in a new message for core that will let you replay a channel from a UID. This will let you catch up if your game was briefly offline.

The server will send:

{ 
  "event": "channels/replay",
  "ref": "28523394-6dcf-4c2a-ad1d-2d0ef8bb823b",
  "payload": {
    "channel": "grapevine",
    "since": "b35198e0-07cb-43f9-b644-7bb6640d2c0f"
  }
}

This could then send broadcast messages for anything missed. Since they have a timestamp this might be ok. Alternatively there can be a separate replay response that includes paginated messages.

Self hosted docs

Instead of everyone reading the README, the server should host its own docs.

Mix Failure at startup

Trying to follow the instructions for getting this working, I have exactly the required software versions specified. I see this:

[master] grapevine $ mix deps.get
** (CompileError) config/config.exs:1: module Config is not loaded and could not be found
    (elixir) lib/code.ex:232: Code.eval_string/3
    (mix) lib/mix/config.ex:220: Mix.Config.eval!/2

Is the assumption that one will set this up via vagrant or docker instead of locally?

• • • • •

I kind of expected a bin/setup or something similar to get started working on an issue. :)

Moderation

Moderation is hard. Let's start thinking about how to tackle this. Raisin will be the tool that handles this.

Some ideas, note that they may not be implemented - food for thought for now:

  • fuzzing bad words similar to blizzard's games
  • flag a player on a game as "ghosted", when ghosted all communication from them is silently dropped
  • disable a game entirely, silently or dropping the connection entirely
  • channels that are publicly displayed get word fuzzing, hidden channels are more open

List connected games

Games that have an open socket(s) should be listed as online and have a websocket query for it.

Associated links for Games

In order to provide extra information for your game, you should be able to attach links, things such as an interview on Titans of Text, a well thought out review, game help, etc.

  • Attach links similar to connections, from your management page
  • Ask for a URL, try to guess title and description based on the URL's metadata
  • Edit a link to set poorly parsed results
  • Rearrange the display order of links
  • View on the game's profile page

Secure Telnet option not connecting

I am able to securely telnet from Mudlet to my test game with ageofelements.org 8680. This does not function for Grapevine at this time when I register this address into Grapevine. This is not a high priority for me as normal telnet functions just fine, but I wanted to register the issue. Thank you!

Hosted Sites

Allow games to have a hosted site, which has a nice display and access to the web client.

  • CNAME support
  • View a simple hosted site
  • List basic information
    • about
    • how to connect
  • Create pages
    • title
    • body
    • tags
  • Blog

Interactive Modal Support

As a MUD owner
I want to be able to generate modal window on the web-client
So that I can create a richer experience for my web-client players

Send tells to remote players

Send a remote player a tells. This should be placed behind a tells support flag. Suggested you also support the players flag.

tells/send

To send a new message, use this event.

{
  "event": "tells/send",
  "ref": "5c528fc3-cb9e-4867-98ea-6e235594241e",
  "payload": {
    "from": "Player",
    "game": "MidMUD",
    "player": "eric",
    "sent_at": "2018-07-17T13:12:28Z",
    "message": "hi"
  }
}
  • from - local player
  • game - remote game, this is the short name of a game
  • player - remote player
  • sent_at - ISO8601 formatted timestamp of when the message was sent, in the UTC timezone
  • message - the message

If the game and player is online on Gossip, then the message will be forwarded. A ref is required.

{
  "event": "tells/send",
  "ref": "5c528fc3-cb9e-4867-98ea-6e235594241e",
  "status": "success"
}
{
  "event": "tells/send",
  "ref": "5c528fc3-cb9e-4867-98ea-6e235594241e",
  "status": "failure",
  "error": "game offline"
}

Possible errors:

  • Game is offline
  • Game is not receiving tells (not supported, but connected)
  • Player is offline

tells/receive

When the game receives a new direct message from another game on the network, this event will be pushed.

{
  "event": "tells/receive",
  "ref": "d4a08749-acbe-45ab-bc0f-51609fd6b95b",
  "payload": {
    "game": "AMud",
    "from": "Player",
    "player": "eric",
    "sent_at": "2018-07-17T13:12:28Z",
    "message": "hi"
  }
}
  • from - remove player
  • game - remote game, this is the short name of a game
  • player - local player
  • sent_at - ISO8601 formatted timestamp of when the message was sent, in the UTC timezone
  • message - the message

Tagging Games

In order to group games together, let's have a tagging system. This will be managed by admins and not freeform. This will allow for highly curated tags.

  • Admins can create a standard set of tags that games can pick from, CRUD
  • Tags have a category, this can be a simple string and admins can make sure to match it up properly for other tags
  • While managing your game, select which tags match
  • View tags on the game's profile page
  • View tags in the index
  • Filter game listing by tags

Extra:

  • Upload an icon for each tag

Example tags:

  • Playstyle: Hack 'n' Slash
  • Playstyle: Role Playing Intensive
  • Genre: Fantasy
  • Genre: Sci-Fi

Add view count to events

Whenever an event is viewed as a webpage, increase it's view count. Display in the admin panel.

Play sound in the web client

MUD Client Media Protocol

Inspired by MSP

Also on Mudlet's Wiki

Enable Media

The client will enable sound by including "Client.Sounds 1" as part of the initial Core.Supports messages.

Core.Supports.Set ["Client.Media 1", ...]

Set Default Values

You can set the default values for certain other messages by sending this message.

Client.Media.Default {
  "url": "https://www.example.com/sounds/"
}
  • url: required, string, base URL to fetch the sound file at

Play File

Send a Client.Sounds.Start event to start playing a sound or music file.

Client.Media.Start {
  "key": "zone-background-music",
  "url": "https://example.com/",
  "name": background.mp3",
  "type": "music",
  "tag": "zones",
  "volume": 100,
  "loops": -1,
  "continue": true,
  "priority": 51
}
  • key: required, string, this is a unique key that will identify the sound in future events
  • url: required, string, base URL to fetch the sound file at
  • name: required, string, file name to add to the base url above, this should be an mp3
  • type: optional, string, default "sound", options "music" or "sound"
  • tag: optional, string, extra string to help tag sounds for internal organization
  • volume: optional, integer, 1-100, default 50, relative volume that the sound will play at
  • loops: optional, integer, -1 or 1+, default 1, number of loops that the sound file should play before stopping, -1 continues until a stop event is sent
  • continue: optional, boolean, default true, if true the sound file will continue playing if already playing, false will restart the sound
  • priority: optional, integer, 1-100, default 50, if a higher priority sound comes in, all sounds of the same type under it will stop playing

Stop File

To stop a media file that may be running, send an event with a filter to stop all matching media.

Client.Media.Stop {
  "key": "zone-background-music",
  "type": "music",
  "priority": 50,
  "tag": "zones",
  "name": "background.mp3"
}

The filter above will check all active media files, and any matching media is stopped. For key, type, tag, and name, a simple string match is performed. For priority, anything below or equal to the sent value is matched.

Media must match all sent values in order to be considered a match and stopped.

To stop all sounds, send an event with no key.

Client.Media.Stop {}

Examples

Play a combat sound.

Client.Media.Start {
  "key": "combat",
  "url": "https://example.com/sword-swing.mp3"
}

Interrupt your sword swing with the enemy blocking.

Client.Media.Start {
  "key": "block",
  "url": "https://example.com/block.mp3",
  "priority": 51
}

Play zone background music.

Client.Media.Start {
  "key": "zone-background-music",
  "url": "https://example.com/background.mp3",
  "type": "music",
  "loops": -1,
  "continue": true
}

Migrate to a new zone.

Client.Media.Start {
  "key": "zone-background-music",
  "url": "https://example.com/background2.mp3",
  "type": "music",
  "loops": -1,
  "continue": false
}

Continue playing the zone's background, but add in a passing storm.

Client.Media.Start {
  "key": "storm",
  "url": "https://example.com/background-storm.mp3",
  "type": "music",
  "loops": -1,
  "continue": false
}

Stop the storm, the zone background will continue playing.

Client.Media.Stop {
  "key": "storm"
}

Re-arrange the settings page

Move the games listing to the sidebar, always display the full list, Games becomes a header, each game is a link to the manage page

Prometheus metrics

Set up prometheus metrics:

  • Gauge: Connected games
  • Gauge: Connected sockets
  • Messages being sent

Display the last seen connecting version of your game

On connect optionally send the client's known version of Gossip, save this.

{
  "event": "authenticate",
  "payload": {
    "client_id": "client id",
    "client_secret": "client secret",
    "version": "1.0.0",
    "supports": ["channels"],
    "channels": ["gossip"],
    "user_agent": "ExVenture 0.23.0"
  }
}

This will let the app display a notification for the game's admin to let them know new features of Gossip are available. For instance when direct messaging is added (#2) it will be more obvious.

If the version is not sent, default to 1.0.0.

Restart events

Send out an incoming restart event.

{
  "event": "restart",
  "payload": {
    "downtime": 15
  }
}

downtime is the number of seconds the server expects to be down. The server should stagger each connected socket +/- a random amount of seconds from the expected value given.

Once the event is sent the server may stop at any moment.

Track referrals to a hosted client

In order for game admins to see how people land on the web client, allow for tracking a referral query parameter and display via a simple report.

Report:

  • Select from/to range, default to the last week
  • Filter down to only referrals in that time period
  • Display a table with referral and the count in that time period

Add UID to messages on a channel

We should add a UID and timestamp field to messages. The server would add both of these fields when broadcasting out.

  • timestamp will be a ISO8601 timestamp in UTC
  • uid will be a string that uniquely identifies this message

Example

Sending stays as normal:

{
  "event": "channels/send",
  "ref": "28523394-6dcf-4c2a-ad1d-2d0ef8bb823b",
  "payload": {
    "channel": "grapevine",
    "name": "Player",
    "message": "Hello everyone!"
  }
}

Games listening on your channel now receive these extra fields.

{
  "event": "channels/broadcast",
  "ref": "89036074-446f-41ab-b87a-44ef1f962f2e",
  "payload": {
    "uid": "a61a2913-84c6-410d-bee0-e2bd8d34374e",
    "timestmap": "2019-11-22T18:00:00Z",
    "channel": "grapevine",
    "message": "Hello everyone!",
    "game": "ExVenture",
    "name": "Player"
  }
}

allow inserting line breaks in the mud client text input

I have noticed you can't send a multiline message using the client. The enter button sends the message, which is fine, but It would be good to be able to use shift+enter to insert a new line.

I've also noticed that if you copy some text with line breaks and you paste it into the client input the line breaks get replaced by whitespaces.

My use case for this is the game I'm developing where users may enter descriptions for items or rooms they create. Splitting the message in different lines is great to make the descriptions more readable. I believe it would be useful for other games too.

Calendar view for events

The current /events page should look like a calendar.

  • Default page shows the current full week, the previous full week, and the next 4 weeks to have some context around the current day
  • Next/previous links will move around by months

New event to get currently connected games and players

Part of the players support flag.

Request:

{
  "events": "players/status",
  "ref": "c8cbaef2-b6e9-4110-b712-a312aee9e7d4"
}

ref is required.

Response:

{
  "events": "players/status",
  "ref": "c8cbaef2-b6e9-4110-b712-a312aee9e7d4",
  "payload": {
    "MidMUD": ["eric"],
    "Myelin": ["Admin"],
    "Apotheosis": ["Adam"]
  }
}

Track web client sessions for game admins

A simple report to see the number of opened web client sessions for a time range.

Report:

  • Select from/to range, default to the last week
  • Filter down to only sessions opened in that time period
  • Display a table with the sessions and how long they were open

Ties with #70

Multiple game owners

Switch games from belongs_to :user to has_many :admins

Create a new join table that let's multiple users administrate a game. No special roles at the moment, all admins can manage the full aspect of a game.

Maybe we keep the current belongs_to :user and rename to be the "owner" so we can tell who originally made it and maybe who should have slight extra power in the future. Other systems have the concept of owner and admin, and just admin.

Criteria:

  • Create table for the users <-> admin <-> game join
  • Page to manage game admins
  • Update any authentication check to check for being a game admin instead of just the single owner

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.