Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ module.exports = {
meet: 'writable',
meetLowLevel: 'writable',
meetHighLevel: 'writable',
savePlayer: 'writable',
zoneManager: 'writable',
notificationMessage: 'writable',
nippleManager: 'writable',
Expand Down
9 changes: 8 additions & 1 deletion app/settings-dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,14 @@
"allowAccountCreation": "all",
"allowLevelCreation": true,
"allowProfileEdition": true,
"contactURL": ""
"contactURL": "",
"guest": {
"changeSkin": false,
"talkToUsers": false,
"useEntity": false,
"useMeetingRoom": false,
"useMessaging": false
}
},

"passwordless": false,
Expand Down
4 changes: 3 additions & 1 deletion core/client/entity-manager.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { guestAllowed, permissionTypes } from '../lib/misc';

const entityAnimations = {
spawn: (sprite, scene) => {
sprite.scaleY = 1.35;
Expand Down Expand Up @@ -146,7 +148,7 @@ entityManager = {

allowedToUseEntity(entity) {
const user = Meteor.user();
if (user.profile.guest) return false;
if (user.profile.guest && !guestAllowed(permissionTypes.useEntity)) return false;

if (!entity.requiredItems?.length) return true;

Expand Down
18 changes: 8 additions & 10 deletions core/client/lemverse.hbs.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,27 @@
</div>

{{#if guest}}
{{> formAccount visible=loading}}
{{> formAccount visible=loading}}
{{else}}
{{> editToolbox}}
{{> radialMenu }}
{{/if}}

{{#unless guest}}
{{> editToolbox}}

<div class="js-streams">
{{#each remoteUser in allRemoteStreamsByUsers}}
{{> remoteStream remoteUser=remoteUser }}
{{> remoteStream remoteUser=remoteUser }}
{{/each}}
</div>

{{> userPanel loading=loading}}
{{> radialMenu }}
{{/unless}}

{{> notificationButton}}
{{> zoneNameToaster}}
{{> notificationButton}}

{{> modalContainer }}

<div class="modules">
{{#each module in modules}}
{{> Template.dynamic template=module}}
{{> Template.dynamic template=module}}
{{/each}}
</div>
</div>
Expand Down
10 changes: 8 additions & 2 deletions core/client/lemverse.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Phaser from 'phaser';
import audioManager from './audio-manager';
import meetingRoom from './meeting-room';
import { setReaction } from './helpers';
import { guestAllowed, permissionTypes } from '../lib/misc';
import initSentryClient from './sentry';

initSentryClient();
Expand Down Expand Up @@ -122,15 +123,20 @@ Template.lemverse.onCreated(function () {
game.scene.add('BootScene', BootScene, true);

Tracker.nonreactive(() => {
if (!Meteor.user()?.profile.guest) peer.createMyPeer();
const user = Meteor.user();
if (!user) return;
if (user.profile.guest && !guestAllowed(permissionTypes.talkToUsers)) return;

peer.createMyPeer();
});
});

this.autorun(() => {
const { status } = Meteor.status();
Tracker.nonreactive(() => {
const user = Meteor.user();
if (!user || user.profile.guest) return;
if (!user) return;
if (user.profile.guest && !guestAllowed(permissionTypes.talkToUsers)) return;

if (status === 'connected') peer.createMyPeer();
else peer.peerInstance?.disconnect();
Expand Down
13 changes: 13 additions & 0 deletions core/client/lemverse.scss
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,19 @@ button.js-notifications {
}
}

.alert {
padding: 5px 10px;
margin: 20px;
border: 1px solid transparent;
border-radius: 4px;

&.warning {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
}
}

#noty_layout__topLeft {
z-index: 100;
pointer-events: none;
Expand Down
20 changes: 13 additions & 7 deletions core/client/peer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Peer from 'peerjs';
import audioManager from './audio-manager';
import meetingRoom from './meeting-room';
import { canAnswerCall, meteorCallWithPromise } from './helpers';
import { guestAllowed, permissionTypes } from '../lib/misc';

const debug = (text, meta) => {
if (!Meteor.user()?.options?.debug) return;
Expand Down Expand Up @@ -206,6 +207,11 @@ peer = {
}

const peer = await this.getPeer();
if (!peer) {
debug(`createPeerCalls: peer not created`);
return;
}

if (shareAudio || shareVideo) userStreams.createStream().then(stream => this.createPeerCall(peer, user, stream, streamTypes.main));
if (shareScreen) userStreams.createScreenStream().then(stream => this.createPeerCall(peer, user, stream, streamTypes.screen));
},
Expand Down Expand Up @@ -271,10 +277,8 @@ peer = {
onProximityStarted(nearUsers) {
if (!this.isEnabled()) return;

const user = Meteor.user();
if (user?.profile.guest) return; // disable proximity sensor for guest user

nearUsers.forEach(nearUser => {
if (nearUser.profile.guest && !guestAllowed(permissionTypes.talkToUsers)) return;
if (this.isCallInState(nearUser._id, callAction.open)) return;
this.cancelWaitingCallAction(nearUser._id);

Expand Down Expand Up @@ -465,8 +469,10 @@ peer = {

async createMyPeer(skipConfig = false) {
if (this.isPeerValid(this.peerInstance)) return this.peerInstance;
if (!Meteor.user()) throw new Error(`an user is required to create a peer`);
if (Meteor.user().profile.guest) throw new Error(`peer is forbidden for guest account`);

const user = Meteor.user();
if (!user) throw new Error(`an user is required to create a peer`);
if (user.profile.guest && !guestAllowed(permissionTypes.talkToUsers)) throw new Error(`You need an account to talk to other users`);

this.peerLoading = true;
const result = await meteorCallWithPromise('getPeerConfig');
Expand Down Expand Up @@ -500,8 +506,8 @@ peer = {
else if (peerErr.type === 'unavailable-id') lp.notif.error(`It seems that ${Meteor.settings.public.lp.product} is already open in another tab (unavailable-id)`);
else if (peerErr.type === 'peer-unavailable') {
const userId = peerErr.message.split(' ').pop();
const user = Meteor.users.findOne(userId);
lp.notif.warning(`User ${user?.profile.name || userId} was unavailable`);
const userUnavailable = Meteor.users.findOne(userId);
lp.notif.warning(`User ${userUnavailable?.profile.name || userId} was unavailable`);
} else lp.notif.error(`Peer ${peerErr} (${peerErr.type})`);

debug(`peer error ${peerErr.type}`, peerErr);
Expand Down
1 change: 0 additions & 1 deletion core/client/scenes/scene-world.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ WorldScene = new Phaser.Class({
this.events.on('postupdate', this.postUpdateMethod, this);
this.events.once('shutdown', this.shutdownMethod, this);
this.scale.on('resize', this.resizeMethod, this);
hotkeys.setScope('guest');

// custom events
window.addEventListener(eventTypes.onZoneEntered, onZoneEntered);
Expand Down
13 changes: 8 additions & 5 deletions core/client/ui/form-log-in.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,14 @@ const onSubmit = template => {
Meteor.loginWithPassword(email, password, err => {
if (err) { lp.notif.error('Incorrect login or password'); return; }

if (Meteor.user().profile.levelId === ghostLevelId) {
savePlayer({
x: ghostX,
y: ghostY,
direction: ghostDirection,
const user = Meteor.user();
if (user.profile.levelId === ghostLevelId) {
Meteor.users.update(user._id, {
$set: {
'profile.x': ghostX,
'profile.y': ghostY,
'profile.direction': ghostDirection,
},
});
}

Expand Down
2 changes: 1 addition & 1 deletion core/client/ui/form-sign-in.hbs.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{{#if eq getStep 1}}
<div class="step">
<h1 class="title">Welcome to {{settings.public.lp.product}}</h1>
<p>You're a ghost, others can see you but you can't interact with them 👻.<br />If you want to join discussion create an account!</p>
<p>You are using a guest account, <u>your actions are limited</u>.<br> Create an account right now to discover all the features of lemverse!</p>
<button type="button" class="js-next-step submit" aria-label="create an account">Join the living world</button><br />
<a href="?mode=login" class="link">Already have an account, go here!</a>
</div>
Expand Down
4 changes: 3 additions & 1 deletion core/client/ui/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ isModalOpen = template => {

toggleModal = (modalName, classes = '') => {
if (Session.get('modal')?.template === modalName) Session.set('modal', null);
else Session.set('modal', { template: modalName, classes });
else if (!Session.get('modal')) Session.set('modal', { template: modalName, classes });
};

closeModal = () => {
if (!isModalOpen()) return;

Session.set('modal', undefined);
toggleUIInputs(false);
};
Expand Down
19 changes: 5 additions & 14 deletions core/client/ui/settings-character.hbs.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<h2 class="h2">Character</h2>
<hr class="separator">
</div>
<div class="settingsCharacter container">
{{#if canEditSkin}}
<div class="renderCharacter">
{{> avatarViewer user=user}}
</div>
Expand Down Expand Up @@ -34,18 +34,9 @@ <h2 class="h2">Character</h2>
</div>
{{/if}}
</div>

<div class="customize-selection">
{{#if or (eq (Session 'settings-character-category') "hair") (eq (Session 'settings-character-category') "accessory")}}
<div class="characters-choices {{#if isBodyPart this._id}}selected{{/if}}">
<div class="js-new-part sprite-character empty-choices" data-id="null"></div>
</div>
{{/if}}
{{#each getAllImages}}
<div class="characters-choices {{#if isBodyPart this._id}}selected{{/if}}">
<img class="js-new-part sprite-character" height="{{zoom height 2}}" width="{{zoom width 2}}" data-id="{{this._id}}" src="{{concat '/api/files/' this.fileId}}" alt="character-part" />
</div>
{{/each}}
{{else}}
<div class="alert warning">
<p><strong>Warning!</strong> You need to create an account to edit your character</p>
</div>
</div>
{{/if}}
</template>
3 changes: 3 additions & 0 deletions core/client/ui/settings-character.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { guestAllowed, permissionTypes } from '../../lib/misc';

Template.settingsCharacter.onCreated(() => {
if (!Session.get('settings-character-category')) Session.set('settings-character-category', 'body');
});
Expand All @@ -11,6 +13,7 @@ Template.settingsCharacter.helpers({
return Meteor.user().profile[Session.get('settings-character-category')] === id;
},
user() { return Meteor.user(); },
canEditSkin() { return !Meteor.user({ fields: { 'profile.guest': 1 } }).profile.guest || guestAllowed(permissionTypes.changeSkin); },
});

Template.settingsCharacter.events({
Expand Down
32 changes: 18 additions & 14 deletions core/client/user-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import audioManager from './audio-manager';
import networkManager from './network-manager';
import meetingRoom from './meeting-room';
import { guestSkin, textDirectionToVector, vectorToTextDirection } from './helpers';
import { guestAllowed, permissionTypes } from '../lib/misc';

const defaultUserMediaColorError = '0xd21404';
const characterPopInOffset = { x: 0, y: -90 };
Expand Down Expand Up @@ -44,15 +45,17 @@ userManager = {
onDocumentAdded(user) {
if (this.characters[user._id]) return null;

const { x, y, guest, direction, name, baseline, nameColor } = user.profile;
const { x, y, guest, direction } = user.profile;

const character = new Character(this.scene, x, y);
character.setData('userId', user._id);
character.direction = direction;
this.characters[user._id] = character;

if (guest) character.updateSkin(guestSkin()); // init with custom skin
else character.setName(name, baseline, nameColor);
if (guest) {
character.updateSkin(guestSkin()); // init with custom skin
character.setName('Guest');
}

window.setTimeout(() => this.onDocumentUpdated(user), 0);

Expand Down Expand Up @@ -111,11 +114,11 @@ userManager = {
const character = this.characters[user._id];
if (!character) return;

const { x, y, direction, reaction, shareAudio, guest, userMediaError, name, baseline, nameColor } = user.profile;
const { x, y, direction, reaction, shareAudio, userMediaError, name, baseline, nameColor, guest } = user.profile;

// update character instance
networkManager.onCharacterStateReceived({ userId: user._id, x, y, direction });
character.showMutedStateIndicator(!guest && !shareAudio);
character.showMutedStateIndicator(!shareAudio);

// is account transformed from guest to user?
if (!user.profile.guest && oldUser?.profile.guest) {
Expand All @@ -137,12 +140,12 @@ userManager = {
}

// update name
const nameUpdated = !guest && (name !== oldUser?.profile.name || baseline !== oldUser?.profile.baseline || nameColor !== oldUser?.profile.nameColor);
if (nameUpdated) character.setName(name, baseline, nameColor);
const nameUpdated = (name !== oldUser?.profile.name || baseline !== oldUser?.profile.baseline || nameColor !== oldUser?.profile.nameColor);
if (nameUpdated) character.setName(name || 'Guest', baseline, nameColor);

const userHasMoved = x !== oldUser?.profile.x || y !== oldUser?.profile.y;
const loggedUser = Meteor.user();
const shouldCheckDistance = userHasMoved && !guest;
const shouldCheckDistance = userHasMoved;

if (user._id === loggedUser._id) {
// network rubber banding
Expand All @@ -166,7 +169,7 @@ userManager = {
}
} else {
if (shouldCheckDistance) userProximitySensor.checkDistance(loggedUser, user);
if (!guest && user.profile.shareScreen !== oldUser?.profile.shareScreen) peer.onStreamSettingsChanged(user);
if (user.profile.shareScreen !== oldUser?.profile.shareScreen) peer.onStreamSettingsChanged(user);
}
},

Expand All @@ -188,7 +191,6 @@ userManager = {
this.controlledCharacter?.enablePhysics(false);
this.controlledCharacter?.enableEffects(false);
this.controlledCharacter = undefined;
hotkeys.setScope('guest');

if (this.scene) {
this.scene.cameras.main.stopFollow();
Expand All @@ -208,9 +210,7 @@ userManager = {

this.scene.cameras.main.startFollow(character, true, 0.1, 0.1);

if (Meteor.user().guest) hotkeys.setScope('guest');
else hotkeys.setScope(scopes.player);

hotkeys.setScope(scopes.player);
this.controlledCharacter = character;
this.controlledCharacter.enableEffects(true);
}
Expand Down Expand Up @@ -257,7 +257,11 @@ userManager = {
this.handleUserInputs();
this.controlledCharacter.running = this.scene.keys.shift.isDown;
this.controlledCharacter.moveDirection = this.inputVector;
this.controlledCharacter.enableChatCircle(peer.isEnabled() && !Session.get('menu') && userProximitySensor.nearUsersCount() > 0);

if (peer.isEnabled() && !Session.get('menu')) {
const nearUsersCount = guestAllowed(permissionTypes.talkToUsers) ? userProximitySensor.nearUsersCount() : userProximitySensor.nearNonGuestUsers().length;
this.controlledCharacter.enableChatCircle(nearUsersCount > 0);
} else this.controlledCharacter.enableChatCircle(false);

const newVelocity = this.controlledCharacter.physicsStep();
const moving = Math.abs(newVelocity.x) > 0.1 || Math.abs(newVelocity.y) > 0.1;
Expand Down
5 changes: 4 additions & 1 deletion core/client/user-proximity-sensor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ userProximitySensor = {

checkDistance(user, otherUser) {
if (user._id === otherUser._id) return;
if (otherUser.profile.guest) return;

const distance = this.distance(user, otherUser);
if (distance < this.nearDistance) this.addNearUser(otherUser);
Expand Down Expand Up @@ -61,6 +60,10 @@ userProximitySensor = {
return this.nearUsersCount() > 0;
},

nearNonGuestUsers() {
return Object.values(this.nearUsers).filter(user => !user.profile.guest);
},

filterNearUsers(userIds) {
return Object.keys(this.nearUsers).filter(userId => userIds.includes(userId));
},
Expand Down
Loading