syncternet-demo
v1.0.7
Published
Syncternet demo page showcasing the Syncternet NPM package. This project is also a good starting point to develop Syncternet plugins.
Readme
Syncternet - Demo Page
This repository is a demo page that uses the syncternet package. If you want to test the package, make changes to the package or create and test your own plugins, do so using the project found at "./dev_modules/syncternet" at this same level. The demo page will reflect all your changes inside the "dev_modules" folder.
Syncternet allows you to interact with other users on the same page. It is a real-time chat and interaction platform that allows you to share your browsing experience with others.
Test the demo
- Go to www.syncternet.com.
- Open the page in your phone and/or or in another browser (use a Private Mode to allow creating a new session).
- See other people around the page and interact with them.
Add Syncternet to your own project
- Install the package with
npm install syncternet. - Load the script in the front-end with
<script src="/syncternet/client"></script>, this route is injected to the Express app when the module is loaded. - Initialize the server with
const syncternet = require('syncternet')andsyncternet.init(app), whereappis your Express app.
Develop your own plugins
npm installnpm run dev
The dev command should build the front-end automatically, otherwise you may need to run a build* command
How it works
Server
Users are stored in the users global variable. This variable maps UUIDs to usernames. Clients are never expected to know other UUID's than themselves. Example structure:
const users = {
"jumping-dog-123": "johnDoe789",
"sleeping-cat-321": "janeDoe987",
// ... and so on for other users
}A matching UUID and username is what makes an user an authenticated one. For this reason UUIDs should never be shared to the client. Public information about the each user for each plugin is stored in public. For example:
const public = {
party: {
"jumping-dog-123": {
pos: {
x: 13,
y: 24,
},
},
"sleeping-cat-321": {
pos: {
x: 88,
y: 75,
},
},
// ... and so on for other users
},
emoticons: {
// ... plugin data
},
// ... and so on for other plugins
}In the example above there are two users at two different locations on the page. Note: Actual Party plugin positions are not stored in X, Y coordinates since each user has a different screen size.
Private information about each user is stored in private. Not necessarily for secret data. It generally contains information to make the plugin work correctly. Private data is never shared to other users. Example:
const public = {
party: {
"jumping-dog-123": {
secondsIdle: 30,
},
"sleeping-cat-321": {
secondsIdle: 5,
},
// ... and so on for other users
},
emoticons: {
// ... plugin data
},
// ... and so on for other plugins
}Plugin definition is found inside the plugins folder. Each folder is a plugin. Three files are defined:
- template.html: Defines the plugin's HTML.
- backend.js: Defines
initandmiddlewarefunctions that are executed on the server-side. - frontend.js: Defines
initandmiddlewarefunctions that are executed on the client-side.
The init function is executed once when the plugin is loaded.
The middleware function is executed each time a message is sent/received.
When a new server is started the server begins listening to msg event. Each case is described below:
"_new"is received: This is a new user. Follow the steps,- Add a new, randomly-generated UUID and username pair to
users. - Server sends
"_keys|<UUID>|<username>"to the client. - Send initial plugin information like
"_plugins|<plugins>".
- Add a new, randomly-generated UUID and username pair to
"_continue|<UUID>|<username>"is received: This is supposedly an existing user. Test ifusers[<UUID>] === <username>. There are 2 possibilities:
It does not match: Don't continue and treat the user as a completely new user as described in previous steps.
It matches: Plugin information is sent as described in previous steps.
In all other cases, plugin information about another user is received and the following steps are taken:
- Server receives
"party|<UUID>|{<plugin data>}". public.party[<UUID>]is updated with plugin data.- Get username from
users. - Server broadcasts
"party|<username>|{<plugin data>}"to all clients including the sender.
- Server receives
Client
Users spin a VUE instance that looks like:
new Vue({
data: {
public: {
// Realtime data, every user has a copy of this
party: {
johnDoe123: {
xpath: "",
pos: { x: 3, y: 5 },
},
janeDoe987: {
xpath: "",
pos: { x: 6, y: 8 },
},
// ... and so on for other users
},
emoticons: {
// ... plugin data
},
// ... and so on for other plugins
},
private: {
// Local data, every user has it own data
UUID: "jumping-dog-123", // What server sent us
username: "johnDoe789", // What we chose
},
},
created() {},
mounted() {},
methods: {},
})When a new user loads a crowwwd page we do:
Check if auth is present in
localStorage.getItem('crowwwd:auth'). There are two possibilities with different responses:- There is no auth data:
"_new"is sent to the server as it a completely new user. - This is a returning user:
"_continue|<UUID>|<username>"is sent.
- There is no auth data:
Begin listening to the
msgevent. This is when a WS message is received. This may serve to update plugin data or for auth purposes. Each case is described below:"_keys|<UUID>|<username>"is received: Update the UUID and username indata.private."_plugins|<plugins>"is received: Add all missing HTMLs. Some of them may be already added manually by the developer.- In all other cases, plugin information about another user is received and the following steps are taken:
"party|<username>|{<plugin data>}"is received. Note how the UUID is not present.- Update
data.public[<username>]with the new information.
In the steps above we checked for localStorage["crowwwd:auth"]. This variable has the structure "<UUID>:<username>". For example: "jumping-dog-123:johnDoe789".
FAQ
...
...
SO resources:
- https://stackoverflow.com/questions/5100376/how-to-watch-for-array-changes
