{"id":13615450,"url":"https://github.com/l3mpire/lemverse","last_synced_at":"2025-04-13T21:31:03.265Z","repository":{"id":37738536,"uuid":"383851017","full_name":"l3mpire/lemverse","owner":"l3mpire","description":"The first-ever coworking metaverse","archived":false,"fork":false,"pushed_at":"2024-06-21T13:55:36.000Z","size":14483,"stargazers_count":150,"open_issues_count":33,"forks_count":41,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-08-02T20:47:05.054Z","etag":null,"topics":["b2b","bootstrap","coworking","entrepreneurship","hacktoberfest","hacktoberfest2022","metaverse","remote","web3"],"latest_commit_sha":null,"homepage":"https://app.lemverse.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/l3mpire.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-07T15:51:14.000Z","updated_at":"2024-07-25T21:26:52.000Z","dependencies_parsed_at":"2023-02-12T17:45:59.362Z","dependency_job_id":"3c489f8e-5b7c-418f-830d-acd6a67048b9","html_url":"https://github.com/l3mpire/lemverse","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l3mpire%2Flemverse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l3mpire%2Flemverse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l3mpire%2Flemverse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l3mpire%2Flemverse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/l3mpire","download_url":"https://codeload.github.com/l3mpire/lemverse/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248786113,"owners_count":21161400,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["b2b","bootstrap","coworking","entrepreneurship","hacktoberfest","hacktoberfest2022","metaverse","remote","web3"],"created_at":"2024-08-01T20:01:13.817Z","updated_at":"2025-04-13T21:30:59.488Z","avatar_url":"https://github.com/l3mpire.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# lemverse\u003cbr\u003e\n\n[![Discord](https://badgen.net/badge/icon/discord?icon=discord\u0026label)](https://discord.gg/uMfZf6T7)\n[![GitHub release](https://img.shields.io/github/v/release/l3mpire/lemverse.svg)](https://GitHub.com/l3mpire/lemverse/releases/)\n[![Open Source? Yes!](https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github)](https://github.com/l3mpire/lemverse)\n[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)\n\n  \u003cimg alt=\"lemverse\" src=\"./app/public/lemverse.png\" width=\"128\" height=\"128\" style=\"margin-top: 10px;\"\u003e\n\n\u003c/div\u003e\n\n# Table of contents\n\n- [What is `lemverse`?](#what-is-lemverse)\n- [What can I do in lemverse?](#what-can-i-do-in-lemverse)\n- [Getting started!](#getting-started)\n- [Deploy in production!](#deploy-in-production)\n- [Useful commands/tricks](#useful-commandstricks)\n- [Assets](#assets)\n- [Star History](#star-history)\n- [License](#license)\n- [Credits](#credits)\n- [Screenshots](#screenshots)\n\n# What is `lemverse`?\n\n- Want to talk with your remote colleagues/friends?\n- Want to have the office you have ever dreamt of?\n- Want to friendly get in touch with people without messaging them?\n- Want to hold virtual conference?\n- Want to walk inside your own coworking office?\n- Want to try something new?\n\nIf you have answered `yes` to one of those questions, then `lemverse` is for you!  \nYou can either launch it locally, on a server or join us at [lemverse.com](https://lemverse.com).\n\n\u003e ℹ️ Can't wait to install lemverse? You can go directly to the [Getting started](#getting-started) section  \n\u003e :warning: For the moment we only focus on compatibility with the Chrome browser\n\n# What can I do in lemverse?\n\n## Tileset Editor\n\nIn order to be able to create your own universe, you will need some tilesets.  \nWe recommend using tileset of 16x16 pixels.\n\nTo upload a new tileset, visit the url `http://localhost:3000/editor` or `http://lemverse.example.com/editor`.\nℹ️ Only people with `admin` role can access this page.\n\nHere are the description of all parts:  \n\u003cimg alt=\"login\" src=\"./app/public/tileset-editor.png\"\u003e\n\n1. As stated, you drag and drop your image file(s) to upload them.\n2. This part is just a reminder about the index of the layers for all tiles.\n\n- Player are between layer `5` and `6`.\n- Default index is `2` and not displayed.\n\n3. This is the list of all tilesets.\n\n- You can either remove the tileset by hitting the cross\n- Rename it, by double clicking on it\n\n4. The view of the tileset\n\n- When you are over a tile, simply hit a number from `0` to `8` to change the layer\n- Layer `2` is the default and thus is not displayed\n- Hit `c` to change the tile to be a collision tile (will be displayed in red)\n\nOnce you have imported the tileset and done some tweak about collision and layering, you can begin to create your universe!\n\n## Character Editor\n\nThis editor takes place at the same place as the tilesets editor.\n\nTo add a new resource, simply drag \u0026 drop over the page.\n\nHere are the description of all parts:  \n\u003cimg alt=\"login\" src=\"./app/public/character-editor.png\"\u003e\n\n1. Different types of filters to change the dropdown\n2. Here is the list of all resources available in the current category\n3. Display of the character part to help you figure out how to categorize it.\n4. The five possible categories to change the part.\n\n## Edit your universe!\n\nFor editing the universe you need to have the right to do so. Luckily for you, outside of production anybody can do it!\n\nTo move to the editor mode, simply it `e` and you should see it!\n\n### Level edit\n\nOnce you hit `e` you will see something like this:  \n\u003cimg alt=\"login\" src=\"./app/public/level-editor.png\"\u003e\n\nHere are the description of all parts:\n\n1. All tiles that you can select. Move your mouse over them and click to select.\n\n- Once it's done, you can click on the map to paste it.\n- You can also select multiple tiles at the same time!\n\n2. You did mess up and want to clean up things?\n\n- You can hit `cmd+z` to undo what you just did.\n- Or use the `eraser` tool to remove one layer (shortcut from `0` to `8`) or hit `c` to remove all layers upon selection.\n\n3. It's the dropdown of all your tilesets. You can select another one to be able to copy/paste other tiles.\n4. Information about your current pointer on map.\n\n- You have the position\n- Information about the different tiles on each layers (useful to use the right `eraser` layer)\n\n### Zone edit\n\nOnce you hit `e` again you will see something like this:  \n\u003cimg alt=\"login\" src=\"./app/public/zone-editor.png\"\u003e\n\nYou can add a zone then select on the map the top left followed by the bottom right corner of the new zone.\n\nIf you want to edit a zone, simply click on either corner coordinate then click on the map.\n\n\u003e ℹ️ Press \"alt\" or \"option\" during edition to snap world coordinates to tiles coordinates\n\nEach zone can be configured to make more things.  \nTo edit those information, simply click on the name of the room (bold text).\n\n```jsonc\n{\n  \"adminOnly\": false|true, // Put true to restrict the zone to administrators\n  \"name\": \"PianoSession\", // Name displayed when you enter in the zone\n  \"hideName\": false|true, // Shows/hides the name of the zone when the user enters it\n  \"roomName\": \"\", // Open a jitsi room with the given name, leave empty to do nothing\n  \"teleportEndpoint\": \"\", // Coordinate \"640,480\" where unauthorized people will be teleported once enter restricted zone\n  \"unmute\": false|true, // Automatically unmute jitsi mic (useful to unmute people on platform in conference room)\n  \"unhide\": false|true, // Automatically show cam in jitsi (useful to show people face automatically on platform in conference room)\n  \"url\": \"https://mczak.com/code/piano/pianoframe\", // If present, this will popup an iframe with this url inside\n  \"fullscreen\": false|true, // Set the iframe if full screen or not\n  \"targetedLevelId\": \"\", // Used for teleport zone. Should be the id of the level to teleport to.,\n  \"inlineURL\": \"https://status.lemlist.com|\u003cp\u003eMy custom text\u003c/p\u003e\", // Pop-in content with URL or HTML content\n  \"disableCommunications\": false|true, // Disabling all communications for the user inside the zone\n  \"yt\": false|true, // If the \"url\" attribute is a YouTube video it allows its integration without blocking\n  \"requiredItems\": [\"itm_x\", \"itm_y\"], // Items required to enter the zone\n  \"spawn\": false|true, // Mark this zone as the starting zone, users will be able to enter the level from here\n  \"popInConfiguration\": {\n    \"position\": \"top\", // Optional: Pop-in position on the zone (center, left, right, bottom or top), set \"relative\" for custom position using \"x\" \u0026 \"y\" (default center)\n    \"x\": 0, // Optional: Relative position from the zone's center on X (you need to set \"position\" to \"relative\")\n    \"y\": -60, // Optional: Relative position from the zone's center on Y (you need to set \"position\" to \"relative\")\n    \"width\": 120, // Optional: Custom width\n    \"height\": 45, // Optional: Custom height\n    \"className\": \"wood-style welcome with-arrow tooltip acid fade-in animated-text\" // Optional: List of CSS classes to customize pop-in's style\n  }\n}\n```\n\n### Entity edit\n\nAn entity is something dynamic outside the map that can be interacted with and animated unlike the world map.\n\nIt is possible to do many things with it like :\n\n- Create doors\n- Add unique/customized elements\n- Create interactions\n- ...\n\nFor game developers, an entity should be seen as a simple identifier on which components are added.\n\nFor the moment it is possible to create entities only from a JSON configuration, some parameters can be modified directly with an interface.\n\nHere is the structure of a basic JSON for a door:\n\n```jsonc\n{\n  // Main data\n  \"_id\": \"ent_x\",\n  \"name\": \"Door\",\n  \"levelId\": \"lvl_z\",\n  \"x\": 0,\n  \"y\": 0,\n\n  // 0 = no action / 1 = actionable / 2 = pickable (will be stored in the user inventory)\n  \"actionType\": 0,\n\n  // optional: Linked entity, useful to create triggers\n  \"entityId\": \"ent_y\",\n\n  // States available (optional)\n  \"state\": \"off\",\n  \"states\": {\n    \"off\": {\n      \"animation\": \"close\", // animation to play when entering this state\n      \"collider\": {\n        \"enable\": true // enable the collider\n      }\n    },\n    \"on\": {\n      \"animation\": \"open\",\n      \"collider\": {\n        \"enable\": false\n      }\n    }\n  },\n\n  // Entity representation in the simulation (optional)\n  \"gameObject\": {\n    \"scale\": 1,\n\n    // Sprite component (optional)\n    \"sprite\": {\n      \"key\": \"image_unique_id\",\n      \"path\": \"image_url|sprite_sheet_url\",\n      \"frameWidth\": 32, // optional: set frame width on the spritesheet\n      \"frameHeight\": 48 // optional: set frame height on the spritesheet\n    },\n\n    // Animation component (optional)\n    \"animations\": {\n      \"open\": {\n        \"repeat\": 0,\n        \"start\": 0,\n        \"end\": 5\n      },\n      \"close\": {\n        \"repeat\": 0, // -1 to animate endlessly, 0 to animate once, x to animate x times\n        \"start\": 5, // starting animation on the\n        \"end\": 0 // ending frame\n      }\n    },\n\n    // Physics component (optional)\n    \"collider\": {\n      \"radius\": 15, // use \"radius\" to create a circle. Use \"width\" and \"height\" to create a rectangle\n      \"offsetX\": -15, // collider offset on X\n      \"offsetY\": -30, // collider offset on Y\n      \"immovable\": false, // static or dynamic physic body\n      \"dragX\": 0.05, // drag on x = \"friction\"/velocity slowdown speed\n      \"dragY\": 0.05, // drag on y = \"friction\"/velocity slowdown speed\n      \"collideTilemap\": true // enable or disable collision with walls\n    }\n  }\n}\n```\n\n## Shortcuts in lemverse\n\nIn lemverse you have only few but useful shortcuts!\n\n### Who's here and where?\n\nWhat to know more about the `explorers` in the same universe?  \nHit `Tab` and you will see them! And maybe ghosts...\n\n\u003cimg alt=\"login\" src=\"./app/public/tab.png\"\u003e\n\nOnly `admin` can see others admin (with the 👑).  \nYou can allow other users to edit your universe by clicking on the hammer and spanner 🛠.\n\n### Reactions\n\nIf you want to use pre-defined reactions, you can hit from `1` to `5` and it will display an emoji on top of your character.  \nIf you want to use a custom one, go to the settings to add one, and use `l` to activate it!\n\n### Share what you want!\n\nActivating camera, sharing screen can be annoying with the mouse itself, so simply use `shift`+`1` (to `4`) to switch the state of the options.\n\n### Editing the level\n\nLike described in the previous part, we must use `e` to launch the edit mode.\n\n### Shout out to your surrounding!\n\nLike in real life, you can shout around you.  \nWe restricted this to the zone you are currently in.\n\nTo do so, simply hit `r` and speak!  \nOnce you finished, just release the touch and your message will be sent to everybody and played instantly.\n\n### Leave a message!\n\nIt may happen that a user isn't available to chat. Fortunately, it's possible to leave a message for the user.\n\nThe use is very simple, you just have to go near the person to whom you want to leave a message and then press the `p` key, speak and release the key.\n\nThe user will then receive a notification.\nYou can also open your notification list and listen to old messages using `cmd/ctrl + 5`.\n\n## How to create a new universe?\n\nTo create a new universe you need to add a document in the `levels` collection.\n\nSimply run the following command in your browser console:\n\n```js\nLevels.insert({ _id: Levels.id(), name: \"My test universe\" });\n```\n\nFor the `level` structure, here are an explanation of all fields:\n\n```jsonc\n{\n  \"_id\": \"lvl_XXXXXXXX\", // Id of the level (useful for TP)\n  \"name\": \"My test universe\", // Name of your universe\n  \"spawn\": {\n    // Spawn position in level\n    \"x\": 42,\n    \"y\": 7\n  },\n  \"skins\": {\n    \"guest\": {\n      // Guest Avatar\n      \"body\": \"chr_11111111111111111\", // Id \"characters\" collection\n      \"accessory\": \"chr_22222222222222222\", // Id in \"characters\" collection (Optional)\n      \"hair\": \"chr_33333333333333333\", // Id in \"characters\" collection\n      \"eye\": \"chr_44444444444444444\", // Id in \"characters\" collection\n      \"outfit\": \"chr_55555555555555555\" // Id in \"characters\" collection\n    },\n    \"default\": {\n      // Default Avatar when user create account\n      \"body\": \"chr_11111111111111111\", // Id \"characters\" collection\n      \"accessory\": \"chr_22222222222222222\", // Id in \"characters\" collection (Optional)\n      \"hair\": \"chr_33333333333333333\", // Id in \"characters\" collection\n      \"eye\": \"chr_44444444444444444\", // Id in \"characters\" collection\n      \"outfit\": \"chr_55555555555555555\" // Id in \"characters\" collection\n    }\n  }\n}\n```\n\nYou can use the level id everywhere it's useful, like in the `defaultLevelId` property in `settings.json` (More information below).\n\n## Tell me more about `settings.json`!\n\nThis file regroups all information about the settings.  \nPlease note, that as stated in section `Deploy in production`, there is an additional file with sensitive information that will be merged with the one in the repository.\n\nℹ️ It's better to copy the file `_settings.json` available in the app folder instead of copying the excerpt below\n\n\u003cdetails\u003e\n  \u003csummary\u003eClick to toggle contents of _settings.json\u003c/summary\u003e\n  \u003cp\u003e\n\n```jsonc\n{\n  \"public\": {\n    \"lp\": {\n      \"product\": \"lemverse\",\n      \"process\": \"main\",\n      \"helpURL\": \"https://lempire.notion.site/lempire/lemverse-Fire-Escape-920063782c4243f69d85ed0a4f65cac3\",\n      \"gods\": [], // List of gods (can use remote command) like \"usr_11111111111111111\"\n      \"production\": true,\n      \"staging\": false,\n      \"enableLogClient\": false\n    },\n\n    \"debug\": false,\n\n    \"defaultReaction\": \"❤️\",\n\n    \"zoom\": {\n      \"default\": 1, // Default zoom level\n      \"min\": 0.6, // Minimum zoom level\n      \"max\": 1.6, // Maximum zoom level\n      \"factor\": 0.001, // Zoom factor (increase to zoom faster, decrease to zoom slower)\n      \"maxDelta\": 100, // Maximum zooming delta\n      \"pinchDelta\": 4 // Delta zoom on mobile pinch\n    },\n\n    \"peer\": {\n      // Settings about webrtc connection\n      \"answerMaxAttempt\": 5,\n      \"answerDelayBetweenAttempt\": 750,\n      \"avatarAPI\": \"https://picsum.photos/320/240?cat\u0026sig=[user_id]\", // Avatar when users do not share their camera\n      \"callDelay\": 250, // Delay before a call is started, useful to avoid a call when you pass by someone\n      \"delayBeforeClosingCall\": 1000,\n      \"sounds\": {\n        \"hangUp\": {\n          \"file\": \"webrtc-out.mp3\",\n          \"volume\": 0.2\n        },\n        \"incomingCall\": {\n          \"file\": \"webrtc-in.mp3\",\n          \"volume\": 0.2\n        }\n      }\n    },\n\n    \"meet\": {\n      // Jitsi settings\n      \"serverURL\": \"meet.jit.si\",\n      \"roomDefaultName\": \"lemverse\"\n    },\n\n    \"character\": {\n      // Settings to handle velocity of the character\n      \"walkSpeed\": 180,\n      \"runSpeed\": 720,\n      \"sensorNearDistance\": 75, // Distance required before starting a call with an user\n      \"sensorFarDistance\": 85, // Distance required before closing a call with an user\n      \"nameColors\": {\n        // Colors available for user name (shown in the dropdown list in the user profile), if not provided a color picker is displayed\n        \"white\": [\"0xffffff\", \"0xffffff\", \"0xffffff\", \"0xffffff\"], // A color gradient is set using the following order: [topLeft, topRight, bottomLeft, bottomRight]\n        \"orange\": [\"0xfc9729\", \"0xfc9729\", \"0xf69831\", \"0xf69831\"],\n        \"red\": [\"0xf15739\", \"0xf15739\", \"0xee5c3b\", \"0xee5c3b\"],\n        \"yellow\": [\"0xf4c918\", \"0xf4c918\", \"0xdbb92a\", \"0xdbb92a\"],\n        \"beige\": [\"0xedc993\", \"0xedc993\", \"0xe5bf8a\", \"0xe5bf8a\"],\n        \"green\": [\"0xabdf3a\", \"0xabdf3a\", \"0xa1cb44\", \"0xa1cb44\"],\n        \"lightGreen\": [\"0x52d8a2\", \"0x52d8a2\", \"0x57cfa0\", \"0x57cfa0\"],\n        \"blue\": [\"0x2394d9\", \"0x2394d9\", \"0x3199da\", \"0x3199da\"],\n        \"lightBlue\": [\"0xa4e2fb\", \"0xa4e2fb\", \"0xa5dbf0\", \"0xa5dbf0\"],\n        \"pink\": [\"0xe584e1\", \"0xe584e1\", \"0xf291f0\", \"0xf291f0\"],\n        \"purple\": [\"0xb558e1\", \"0xb558e1\", \"0xfa8ff8\", \"0xfa8ff8\"]\n      }\n    },\n\n    \"permissions\": {\n      \"allowAccountCreation\": \"all\", // all for everyone, none for no-one, except:lvl_xxx to block a level\n      \"allowLevelCreation\": true,\n      \"allowProfileEdition\": true, // Shows 'My account' tab in the user settings menu\n      \"allowFormLogin\": true, // Disable to only allow JWT login (make sure to configure 'jwtAuthSecret' in this case)\n      \"contactURL\": \"\"\n    },\n\n    \"passwordless\": false, // Activate to use magic link emails instead of password for log-in\n\n    \"assets\": {\n      // Assets configuration\n      \"character\": {\n        \"frameWidth\": 16,\n        \"frameHeight\": 32,\n        \"formats\": {\n          \"w-384\": {\n            // Modern interiors: old format\n            \"animations\": {\n              \"run\": {\n                \"up\": {\n                  \"frames\": [54, 55, 56, 57, 58, 59],\n                  \"frameRate\": 10,\n                  \"repeat\": -1\n                },\n                \"down\": {\n                  \"frames\": [66, 67, 68, 69, 70, 71],\n                  \"frameRate\": 10,\n                  \"repeat\": -1\n                },\n                \"left\": {\n                  \"frames\": [60, 61, 62, 63, 64, 65],\n                  \"frameRate\": 10,\n                  \"repeat\": -1\n                },\n                \"right\": {\n                  \"frames\": [48, 49, 50, 51, 52, 53],\n                  \"frameRate\": 10,\n                  \"repeat\": -1\n                }\n              }\n            }\n          },\n          \"w-927\": {\n            // Modern interiors: new format\n            \"animations\": {\n              \"run\": {\n                \"up\": {\n                  \"frames\": [120, 121, 122, 123, 124, 125],\n                  \"frameRate\": 10,\n                  \"repeat\": -1\n                },\n                \"down\": {\n                  \"frames\": [132, 133, 134, 135, 136, 137],\n                  \"frameRate\": 10,\n                  \"repeat\": -1\n                },\n                \"left\": {\n                  \"frames\": [126, 127, 128, 129, 130, 131],\n                  \"frameRate\": 10,\n                  \"repeat\": -1\n                },\n                \"right\": {\n                  \"frames\": [114, 115, 116, 117, 118, 119],\n                  \"frameRate\": 10,\n                  \"repeat\": -1\n                }\n              }\n            }\n          }\n        },\n        \"w-896\": {\n          // Modern interiors: new format (for hairs, eyes, outfits, …)\n          \"animations\": {\n            \"run\": {\n              \"up\": {\n                \"frames\": [118, 119, 120, 121, 122, 123],\n                \"frameRate\": 10,\n                \"repeat\": -1\n              },\n              \"down\": {\n                \"frames\": [130, 131, 132, 133, 134, 135],\n                \"frameRate\": 10,\n                \"repeat\": -1\n              },\n              \"left\": {\n                \"frames\": [124, 125, 126, 127, 128, 129],\n                \"frameRate\": 10,\n                \"repeat\": -1\n              },\n              \"right\": {\n                \"frames\": [112, 113, 114, 115, 116, 117],\n                \"frameRate\": 10,\n                \"repeat\": -1\n              }\n            }\n          }\n        }\n      }\n    },\n\n    \"skins\": {\n      // Default skins (can be defined at level)\n      \"guest\": {\n        \"body\": \"chr_H2ARGyiKd8wQ4hQcr\"\n      },\n      \"default\": {\n        \"body\": \"chr_2HARGyiKf8wQ8hQcr\"\n      }\n    },\n\n    \"tos\": {\n      \"terms\": \"\",\n      \"cookies\": \"\",\n      \"privacy\": \"\"\n    }\n  },\n\n  \"forbiddenIPs\": [], // Banned IPs\n\n  \"defaultLevelId\": \"lvl_iLOVEaLOTlemverse\", // Default level Id created at first run.\n  \"defaultKickLevelId\": \"lvl_IamINlemverseJAIL\", // Default level Id when kicking people from current level\n\n  \"respawnEnabled\": true, // Will respawn users to the level spawn area on depending the last logout date\n  \"respawnDelay\": 540, // Spawn users to the level's spawn point after 9 hours. Remove this to disable the respawn\n\n  \"email\": {\n    \"from\": \"The lembot \u003ccontact@domain.com\u003e\"\n  },\n\n  \"jwtAuthSecret\": \"your-256-bit-secret\", // If specified, it allows user login using JWT\n  // For instance: https://app.lemverse.com?token={jwt_token}\n  // Example of the token payload:\n  // {\n  //   \"sub\": \"user@example.com\",\n  //   \"nbf\": 1667563200,\n  //   \"exp\": 1667570400,\n  //   \"level_id\": \"lvl_nENCytZvbcHhxsFSt\"\n  // }\n\n  \"meet\": {\n    \"enableAuth\": false\n  },\n\n  \"peer\": {\n    \"path\": \"/peer\",\n    \"client\": {\n      \"url\": \"peer.example.com\",\n      \"port\": 443,\n      \"secret\": \"******\", // Required for turn server support\n      \"credentialDuration\": 86400,\n      \"config\": {\n        \"iceServers\": [\n          {\n            \"urls\": \"stun:stun.l.google.com:19302\"\n          }\n        ],\n        \"iceTransportPolicy\": \"all\",\n        \"sdpSemantics\": \"unified-plan\"\n      }\n    },\n    // Details about the configuration bellow is available here: https://github.com/peers/peerjs-server#config--cli-options\n    \"server\": {\n      \"port\": 7010,\n      \"key\": \"peerjs\",\n      \"alive_timeout\": 60000,\n      \"expire_timeout\": 5000,\n      \"allow_discovery\": false\n    }\n  },\n\n  // Use AWS s3 bucket to make the app stateless\n  \"s3\": {\n    \"key\": \"AWSKEY\",\n    \"secret\": \"AWSSECRET\",\n    \"bucket\": \"BUCKETNAME\",\n    \"region\": \"eu-west-3\",\n    // Fetch image from a CDN instead of the bucket\n    \"cdn\": {\n      \"url\": \"https://cdn.cloudfront.net/\"\n    }\n  },\n\n  // Use Monti APM\n  \"monti\": {\n    \"appId\": \"\",\n    \"appSecret\": \"\",\n    \"options\": {\n      \"uploadSourceMaps\": false\n    }\n  },\n\n  \"packages\": {\n    // configure external authentication services\n    \"service-configuration\": {\n      // An authentication button with the name of the service will be displayed.\n      // you can automatically trigger the authentication service by passing the idpHint parameter to the url of your server.\n      // example: https://app.lemverse.com/idpHint=custom will automatically trigger the \"custom\" login service\n      \"custom\": {\n        // for pure OAuth identity provider\n        \"buttonBackgroundColor\": \"#ea4335\",\n        \"buttonTextColor\": \"white\",\n        \"clientId\": \"xxxxxxx\",\n        \"type\": \"oauth\",\n        \"custom\": true, // true for custom OAuth2 provider (not for social login)\n        \"hidden\": false, // when true hides the login button, the oauth service remains configured and can be triggered by the url parameter idpHint=custom\n        \"secret\": \"xxxxxxx\",\n        \"authUrl\": \"https://auth.example.org/oauth/authorize\",\n        \"accessTokenUrl\": \"https://auth.example.org/oauth/access_token\",\n        \"identityUrl\": \"https://auth.example.org/oauth/userinfo\",\n        \"serverURL\": \"http://localhost:3000/\",\n        \"responseType\": \"code\",\n        \"loginStyle\": \"redirect\",\n        \"scope\": \"openid\",\n        \"identity\": [\"firstName\", \"lastName\"]\n      },\n      // Social login with preconfigured oauth providers\n      \"twitter\": {\n        \"consumerKey\": \"xxxxxxx\",\n        \"secret\": \"xxxxxxx\",\n        \"type\": \"oauth\",\n        \"custom\": false,\n        \"loginStyle\": \"redirect\"\n      },\n      \"github\": {\n        \"clientId\": \"xxxxxxx\",\n        \"buttonBackgroundColor\": \"#5880ff\",\n        \"buttonTextColor\": \"white\",\n        \"type\": \"oauth\",\n        \"custom\": false,\n        \"secret\": \"xxxxxxx\"\n      },\n      \"google\": {\n        \"clientId\": \"xxxxxxx\",\n        \"buttonBackgroundColor\": \"#5880ff\",\n        \"buttonTextColor\": \"white\",\n        \"type\": \"oauth\",\n        \"custom\": false,\n        \"secret\": \"xxxxxxx\",\n        \"loginStyle\": \"redirect\"\n      },\n      \"facebook\": {\n        \"appId\": \"xxxxxxx\",\n        \"buttonBackgroundColor\": \"#5880ff\",\n        \"buttonTextColor\": \"white\",\n        \"type\": \"oauth\",\n        \"custom\": false,\n        \"secret\": \"xxxxxxx\",\n        \"loginStyle\": \"redirect\"\n      }\n    },\n    \"email\": {\n      \"service\": \"Mailgun\",\n      \"user\": \"postmaster@meteor.com\",\n      \"password\": \"superDuperPassword\"\n    }\n  }\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n# Getting started!\n\nOnce you have cloned the repo:\n\n- Install system dependencies:\n  - [Meteor](https://docs.meteor.com/install.html) (you will need to [install NodeJS](https://nodejs.org/en/download/) if you are using Windows)\n  - GraphicsMagick (most likely available in your package manager, e.g. `brew install graphicsmagick`)\n- Go to `./app/`\n- Install JS dependencies: `meteor npm install`\n- Run the app: `meteor --settings settings-dev.json`\n\nThe app should now be accessible at `http://localhost:3000`, and MongoDB at `mongodb://localhost:3001/meteor`.\n\n## LocalTunnel to debug with other computers\n\nWebrtc is working when on localhost, but if you want to test with another computer you need to have an HTTPS connection.  \nYou will need to use a tunnel to expose you laptop over internet.\n\nWe decided to use [localtunnel](https://github.com/localtunnel/localtunnel).  \nOnce it's installed on an accessible server, setup env variable `LT_DOMAIN` without http(s) so just the domain.\n\nAfter that, simply launch `ROOT_URL=https://lemverse-$(whoami).${LT_DOMAIN} meteor --settings settings-dev.json`.\n\nModify `createMyPeer` in `peer.js` to change the host to `lemverse-peer-USER-DOMAIN` while `USER`=`whoami` and `DOMAIN`=`LT_DOMAIN` env variable.\n\nAccess to your local instance at: `https://lemverse-USER-DOMAIN`.\n\n\u003e :warning: Don't forget to change the port to 443 for peers when using local tunnel\n\n## First login\n\nSimply create your account and voila!  \nYou now have a nice player with everything is black!\n\nThe first user will always be an admin and so has the right to edit the level by pressing `E`.\nIf you are not the first user, you are not admin, and so you can not change anything 😭.  \nLet's change that!\n\nExecute this command and you should become admin:\n\n```js\nremote(\n  `Meteor.users.update(Meteor.userId(), { $set: { roles: { admin: true } }})`\n);\n```\n\nNow enjoy the possibility to edit by simply pressing `E` on you keyboard (see more detail below).\n\nℹ️ In production, to execute the `remote` command you need to add yourself (`Meteor.userId()`) in the admin array in `settings.json` (something like `usr_XXXXXX`) or hide it in the `/usr/local/etc/lemverse.json` (Server side only!).\n\n## Roles\n\nIn lemverse you have different roles:\n\n- `Guest`: Can move everywhere but can not talk, share screen nor listen\n- `User`: Can move everywhere except admin zone (`adminOnly=true`), talk, listen, share screen\n- `Editor`: Same as `User` but can also edit the level\n- `Admin`: Same as `Editor` (for all levels) but can go to every zones and give people `Editor` roles (Through UI)\n- `God`: Same as `Admin` but can also run `remote` command from the console\n\n# Deploy in production!\n\n## Docker images\n\n### Official image\n\nThe official lemverse image is `lempire/lemverse`.  \nIf you want to pull the last version, you should do:  \n`docker pull lempire/lemverse:latest`\n\n### Build\n\n#### Production\n\nTo build the latest version of lemverse, simply run the following command:  \n`docker build . -t lempire/lemverse:latest`\n\n#### Development\n\nTo build from you source without having to install anything, you can run the following command:\n\n`docker build -f Dockerfile.dev . -t lempire/lemverse:dev`\n\nOr run the full dev env with hot reload: `docker-compose up -d`  \nIt take a while to start the server.\nThen every change in the project will be automatically reloaded.\n\n### Deploy\n\nVisit `example/docker-compose-prod` to find the deployment instructions with `docker-compose` stack.\n\n## Slack Notification upon deployment\n\nTo have a slack notification, you need to install the [slack cli](https://github.com/rockymadden/slack-cli) on the workstation from which you will deploy.\n\nYou should also have an environment variable `LEMVERSE_CHANNEL`\n\n# Useful commands/tricks\n\n## Beta flag\n\nIf you want to add feature either not completely finished or just to test for few users, you should use `beta flags`.\n\nTo use the beta flag, you have one function `isLemverseBeta` which can be called either in `.hbs` file as is or in `.js` with `lp.isLemverseBeta('myBetaFlag')`.\n\nThe beta flag are stored in an array named `beta` inside each document in `users` collection.\n\n## Maintenance aka Import/Export data\n\n### Export\n\nSince the data is stored in mongo database, you can simply use the command:\n\n```bash\nmongoexport --db lemverse --collection=tilesets --jsonArray --out lemverse-tilesets.json\n```\n\n### Import\n\nTo import a saved collection, use the following command to import it:\n\n```bash\nmongoimport --db=lemverse --collection=tilesets --host=localhost --port=9001 --file=./lemverse-tilesets.json --drop --jsonArray\n```\n\nThe command will replace the current `tilesets` collection with the data inside the file.\n\n## `remote` command\n\nWithin your browser, you can safely (`god` only) execute command in your backend.\n\nWe provide a command named `remote` that will pass the content to the backend to be executed with `eval`.\n\nFor example, to add a beta flag to yourself execute this command in your browser:\n\n```js\nremote(\n  \"Meteor.users.update({ _id: Meteor.userId() }, { $addToSet: { 'beta': { $each: ['myAwesomeFeature'] } } });\"\n);\n```\n\n## Custom avatars\n\nIt's possible to modify the avatars displayed during a discussion using an image API. To do so, you just have to modify the `settings.json` file.\n\nYou can add dynamic parameters to your URL using `[user_id]` or `[user_name]` to access id and name of the user who requests an avatar.\n\nWebsite with images API :\n\n- [Unsplash](https://source.unsplash.com)\n- [Robohash](https://robohash.org)\n\n\u003e Example with Robohash: `https://robohash.org/[user_name]?set=set4\u0026bgset=bg2\u0026size=320x240`\n\n# Assets\n\nWe use paid assets from [limezu](https://limezu.itch.io/) on [itch.io](https://limezu.itch.io/moderninteriors).\n\nBy default lemverse appears in black because you have no textures in the project, you must go to the editor to upload the different textures.\n\n# Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=l3mpire/lemverse\u0026type=Date)](https://star-history.com/#l3mpire/lemverse\u0026Date)\n\n# License\n\nAGPLv3\n\n# Credits\n\n- [Meteor](https://www.meteor.com/)\n- [Phaser](https://phaser.io/)\n- [Material Design Icons](https://materialdesignicons.com/) for their icons\n- [Everybody who contribute on it](https://github.com/l3mpire/lemverse/graphs/contributors)!\n\n# Screenshots\n\n\u003cimg alt=\"login\" src=\"./app/public/screenshot-login.png\"\u003e  \n\u003cimg alt=\"Enter Zone\" src=\"./app/public/screenshot-zone.png\"\u003e  \n\u003cimg alt=\"I see ghost everywhere!\" src=\"./app/public/screenshot-ghost.png\"\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl3mpire%2Flemverse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fl3mpire%2Flemverse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl3mpire%2Flemverse/lists"}