Merge pull request #1099 from thecodingmachine/visitCard
FEATURE: clicking on another player show a contact card when possible
This commit is contained in:
commit
44c2276952
@ -16,6 +16,11 @@
|
||||
- We now create a GameObject.Text instead of GameObject.BitmapText
|
||||
- now use the 'Press Start 2P' font family and added an outline
|
||||
- As a result, we can now allow non-standard letters like french accents or chinese characters!
|
||||
|
||||
- Added the contact card feature. (@Kharhamel)
|
||||
- Click on another player to see its contact info.
|
||||
- Premium-only feature unfortunately. I need to find a way to make it available for all.
|
||||
- If no contact data is found (either because the user is anonymous or because no admin backend), display an error card.
|
||||
|
||||
- Mobile support has been improved
|
||||
- WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used
|
||||
|
@ -89,7 +89,10 @@ export class GameRoom {
|
||||
public getUserByUuid(uuid: string): User|undefined {
|
||||
return this.usersByUuid.get(uuid);
|
||||
}
|
||||
|
||||
public getUserById(id: number): User|undefined {
|
||||
return this.users.get(id);
|
||||
}
|
||||
|
||||
public join(socket : UserSocket, joinRoomMessage: JoinRoomMessage): User {
|
||||
const positionMessage = joinRoomMessage.getPositionmessage();
|
||||
if (positionMessage === undefined) {
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
JoinRoomMessage,
|
||||
PlayGlobalMessage,
|
||||
PusherToBackMessage,
|
||||
QueryJitsiJwtMessage, RefreshRoomPromptMessage,
|
||||
QueryJitsiJwtMessage, RefreshRoomPromptMessage, RequestVisitCardMessage,
|
||||
ServerToAdminClientMessage,
|
||||
ServerToClientMessage,
|
||||
SilentMessage,
|
||||
@ -74,6 +74,8 @@ const roomManager: IRoomManagerServer = {
|
||||
socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
|
||||
} else if (message.hasEmotepromptmessage()){
|
||||
socketManager.handleEmoteEventMessage(room, user, message.getEmotepromptmessage() as EmotePromptMessage);
|
||||
} else if (message.hasRequestvisitcardmessage()) {
|
||||
socketManager.handleRequestVisitCardMessage(room, user, message.getRequestvisitcardmessage() as RequestVisitCardMessage);
|
||||
}else if (message.hasSendusermessage()) {
|
||||
const sendUserMessage = message.getSendusermessage();
|
||||
if(sendUserMessage !== undefined) {
|
||||
|
22
back/src/Services/AdminApi.ts
Normal file
22
back/src/Services/AdminApi.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
|
||||
import Axios from "axios";
|
||||
|
||||
|
||||
class AdminApi {
|
||||
|
||||
fetchVisitCardUrl(membershipUuid: string): Promise<string> {
|
||||
if (ADMIN_API_URL) {
|
||||
return Axios.get(ADMIN_API_URL + '/api/membership/'+membershipUuid,
|
||||
{headers: {"Authorization": `${ADMIN_API_TOKEN}`}}
|
||||
).then((res) => {
|
||||
return res.data;
|
||||
}).catch(() => {
|
||||
return 'INVALID';
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve('INVALID')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const adminApi = new AdminApi();
|
@ -27,7 +27,7 @@ import {
|
||||
WorldFullWarningMessage,
|
||||
UserLeftZoneMessage,
|
||||
EmoteEventMessage,
|
||||
BanUserMessage, RefreshRoomMessage, EmotePromptMessage,
|
||||
BanUserMessage, RefreshRoomMessage, EmotePromptMessage, RequestVisitCardMessage, VisitCardMessage,
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import {User, UserSocket} from "../Model/User";
|
||||
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
|
||||
@ -51,6 +51,7 @@ import {Zone} from "_Model/Zone";
|
||||
import Debug from "debug";
|
||||
import {Admin} from "_Model/Admin";
|
||||
import crypto from "crypto";
|
||||
import {adminApi} from "./AdminApi";
|
||||
|
||||
|
||||
const debug = Debug('sockermanager');
|
||||
@ -769,6 +770,21 @@ export class SocketManager {
|
||||
emoteEventMessage.setActoruserid(user.id);
|
||||
room.emitEmoteEvent(user, emoteEventMessage);
|
||||
}
|
||||
|
||||
async handleRequestVisitCardMessage(room: GameRoom, user: User, requestvisitcardmessage: RequestVisitCardMessage): Promise<void> {
|
||||
const targetUser = room.getUserById(requestvisitcardmessage.getTargetuserid());
|
||||
if (!targetUser) {
|
||||
throw 'Could not find user for id '+requestvisitcardmessage.getTargetuserid();
|
||||
}
|
||||
const url = await adminApi.fetchVisitCardUrl(targetUser.uuid);
|
||||
|
||||
const visitCardMessage = new VisitCardMessage();
|
||||
visitCardMessage.setUrl(url);
|
||||
const clientMessage = new ServerToClientMessage();
|
||||
clientMessage.setVisitcardmessage(visitCardMessage);
|
||||
|
||||
user.socket.write(clientMessage);
|
||||
}
|
||||
}
|
||||
|
||||
export const socketManager = new SocketManager();
|
||||
|
@ -13,6 +13,8 @@
|
||||
import LoginScene from "./Login/LoginScene.svelte";
|
||||
import {loginSceneVisibleStore} from "../Stores/LoginSceneStore";
|
||||
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
|
||||
import VisitCard from "./VisitCard/VisitCard.svelte";
|
||||
import {requestVisitCardsStore} from "../Stores/GameStore";
|
||||
|
||||
import {Game} from "../Phaser/Game/Game";
|
||||
import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore";
|
||||
@ -73,4 +75,7 @@
|
||||
<HelpCameraSettingsPopup game={ game }></HelpCameraSettingsPopup>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $requestVisitCardsStore}
|
||||
<VisitCard visitCardUrl={$requestVisitCardsStore}></VisitCard>
|
||||
{/if}
|
||||
</div>
|
||||
|
64
front/src/Components/VisitCard/VisitCard.svelte
Normal file
64
front/src/Components/VisitCard/VisitCard.svelte
Normal file
@ -0,0 +1,64 @@
|
||||
<script lang="typescript">
|
||||
import { fly } from 'svelte/transition';
|
||||
import {requestVisitCardsStore} from "../../Stores/GameStore";
|
||||
|
||||
export let visitCardUrl: string;
|
||||
|
||||
function closeCard() {
|
||||
requestVisitCardsStore.set(null);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.visitCard {
|
||||
pointer-events: all;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 515px;
|
||||
margin-top: 200px;
|
||||
|
||||
.defaultCard {
|
||||
border-radius: 5px;
|
||||
border: 2px black solid;
|
||||
background-color: whitesmoke;
|
||||
width: 500px;
|
||||
|
||||
header {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
width: 515px;
|
||||
height: 270px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<section class="visitCard" transition:fly="{{ y: -200, duration: 1000 }}">
|
||||
{#if visitCardUrl === 'INVALID'}
|
||||
<div class="defaultCard">
|
||||
<header>
|
||||
<h2>Sorry</h2>
|
||||
<p style="font-style: italic;">This user doesn't have a contact card.</p>
|
||||
</header>
|
||||
|
||||
<main style="padding: 5px; background-color: gray">
|
||||
<p>Maybe he is offline, or this feature is deactivated.</p>
|
||||
</main>
|
||||
</div>
|
||||
{:else}
|
||||
<iframe title="visitCardTitle" src={visitCardUrl}></iframe>
|
||||
{/if}
|
||||
<div class="buttonContainer">
|
||||
<button class="nes-btn is-popUpElement" on:click={closeCard}>Close</button>
|
||||
</div>
|
||||
|
||||
</section>
|
@ -30,7 +30,7 @@ import {
|
||||
EmoteEventMessage,
|
||||
EmotePromptMessage,
|
||||
SendUserMessage,
|
||||
BanUserMessage
|
||||
BanUserMessage, RequestVisitCardMessage
|
||||
} from "../Messages/generated/messages_pb"
|
||||
|
||||
import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
||||
@ -50,6 +50,7 @@ import {worldFullMessageStream} from "./WorldFullMessageStream";
|
||||
import {worldFullWarningStream} from "./WorldFullWarningStream";
|
||||
import {connectionManager} from "./ConnectionManager";
|
||||
import {emoteEventStream} from "./EmoteEventStream";
|
||||
import {requestVisitCardsStore} from "../Stores/GameStore";
|
||||
|
||||
const manualPingDelay = 20000;
|
||||
|
||||
@ -203,6 +204,8 @@ export class RoomConnection implements RoomConnection {
|
||||
adminMessagesService.onSendusermessage(message.getBanusermessage() as BanUserMessage);
|
||||
} else if (message.hasWorldfullwarningmessage()) {
|
||||
worldFullWarningStream.onMessage();
|
||||
} else if (message.hasVisitcardmessage()) {
|
||||
requestVisitCardsStore.set(message?.getVisitcardmessage()?.getUrl() as unknown as string);
|
||||
} else if (message.hasRefreshroommessage()) {
|
||||
//todo: implement a way to notify the user the room was refreshed.
|
||||
} else {
|
||||
@ -617,4 +620,14 @@ export class RoomConnection implements RoomConnection {
|
||||
|
||||
this.socket.send(clientToServerMessage.serializeBinary().buffer);
|
||||
}
|
||||
|
||||
public requestVisitCardUrl(targetUserId: number): void {
|
||||
const message = new RequestVisitCardMessage();
|
||||
message.setTargetuserid(targetUserId);
|
||||
|
||||
const clientToServerMessage = new ClientToServerMessage();
|
||||
clientToServerMessage.setRequestvisitcardmessage(message);
|
||||
|
||||
this.socket.send(clientToServerMessage.serializeBinary().buffer);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ import type {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import {Character} from "../Entity/Character";
|
||||
import type {PlayerAnimationDirections} from "../Player/Animation";
|
||||
|
||||
export const playerClickedEvent = 'playerClickedEvent';
|
||||
|
||||
/**
|
||||
* Class representing the sprite of a remote player (a player that plays on another computer)
|
||||
*/
|
||||
@ -25,6 +27,10 @@ export class RemotePlayer extends Character {
|
||||
|
||||
//set data
|
||||
this.userId = userId;
|
||||
|
||||
this.on('pointerdown', () => {
|
||||
this.emit(playerClickedEvent, this.userId);
|
||||
})
|
||||
}
|
||||
|
||||
updatePosition(position: PointInterface): void {
|
||||
@ -40,6 +46,6 @@ export class RemotePlayer extends Character {
|
||||
}
|
||||
|
||||
isClickable(): boolean {
|
||||
return false; //todo: make remote players clickable if they are logged in.
|
||||
return true; //todo: make remote players clickable if they are logged in.
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import type {AddPlayerInterface} from "./AddPlayerInterface";
|
||||
import {PlayerAnimationDirections} from "../Player/Animation";
|
||||
import {PlayerMovement} from "./PlayerMovement";
|
||||
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
|
||||
import {RemotePlayer} from "../Entity/RemotePlayer";
|
||||
import {playerClickedEvent, RemotePlayer} from "../Entity/RemotePlayer";
|
||||
import {Queue} from 'queue-typescript';
|
||||
import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer";
|
||||
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
||||
@ -1379,6 +1379,9 @@ ${escapedMessage}
|
||||
addPlayerData.companion,
|
||||
addPlayerData.companion !== null ? lazyLoadCompanionResource(this.load, addPlayerData.companion) : undefined
|
||||
);
|
||||
player.on(playerClickedEvent, (userID:number) => {
|
||||
this.connection?.requestVisitCardUrl(userID);
|
||||
})
|
||||
this.MapPlayers.add(player);
|
||||
this.MapPlayersByKey.set(player.userId, player);
|
||||
player.updatePosition(addPlayerData.position);
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { derived, writable, Writable } from "svelte/store";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export const userMovingStore = writable(false);
|
||||
|
||||
export const requestVisitCardsStore = writable<string|null>(null);
|
||||
|
@ -75,6 +75,14 @@ message EmoteEventMessage {
|
||||
string emote = 2;
|
||||
}
|
||||
|
||||
message RequestVisitCardMessage {
|
||||
int32 targetUserId = 1;
|
||||
}
|
||||
|
||||
message VisitCardMessage {
|
||||
string url = 1;
|
||||
}
|
||||
|
||||
message QueryJitsiJwtMessage {
|
||||
string jitsiRoom = 1;
|
||||
string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map!
|
||||
@ -94,6 +102,7 @@ message ClientToServerMessage {
|
||||
ReportPlayerMessage reportPlayerMessage = 11;
|
||||
QueryJitsiJwtMessage queryJitsiJwtMessage = 12;
|
||||
EmotePromptMessage emotePromptMessage = 13;
|
||||
RequestVisitCardMessage requestVisitCardMessage = 14;
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,6 +268,7 @@ message ServerToClientMessage {
|
||||
RefreshRoomMessage refreshRoomMessage = 17;
|
||||
WorldConnexionMessage worldConnexionMessage = 18;
|
||||
EmoteEventMessage emoteEventMessage = 19;
|
||||
VisitCardMessage visitCardMessage = 20;
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,6 +340,7 @@ message PusherToBackMessage {
|
||||
SendUserMessage sendUserMessage = 12;
|
||||
BanUserMessage banUserMessage = 13;
|
||||
EmotePromptMessage emotePromptMessage = 14;
|
||||
RequestVisitCardMessage requestVisitCardMessage = 15;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,12 @@ import {
|
||||
PlayGlobalMessage,
|
||||
ReportPlayerMessage,
|
||||
EmoteEventMessage,
|
||||
QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, CompanionMessage, EmotePromptMessage
|
||||
QueryJitsiJwtMessage,
|
||||
SendUserMessage,
|
||||
ServerToClientMessage,
|
||||
CompanionMessage,
|
||||
EmotePromptMessage,
|
||||
RequestVisitCardMessage
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import {UserMovesMessage} from "../Messages/generated/messages_pb";
|
||||
import {TemplatedApp} from "uWebSockets.js"
|
||||
@ -333,6 +338,9 @@ export class IoSocketController {
|
||||
socketManager.handleQueryJitsiJwtMessage(client, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
|
||||
} else if (message.hasEmotepromptmessage()){
|
||||
socketManager.handleEmotePromptMessage(client, message.getEmotepromptmessage() as EmotePromptMessage);
|
||||
} else if (message.hasRequestvisitcardmessage()) {
|
||||
|
||||
socketManager.handleRequestVisitCardMessage(client, message.getRequestvisitcardmessage() as RequestVisitCardMessage);
|
||||
}
|
||||
|
||||
/* Ok is false if backpressure was built up, wait for drain */
|
||||
|
@ -24,7 +24,13 @@ import {
|
||||
AdminPusherToBackMessage,
|
||||
ServerToAdminClientMessage,
|
||||
EmoteEventMessage,
|
||||
UserJoinedRoomMessage, UserLeftRoomMessage, AdminMessage, BanMessage, RefreshRoomMessage, EmotePromptMessage
|
||||
UserJoinedRoomMessage,
|
||||
UserLeftRoomMessage,
|
||||
AdminMessage,
|
||||
BanMessage,
|
||||
RefreshRoomMessage,
|
||||
EmotePromptMessage,
|
||||
RequestVisitCardMessage
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
|
||||
import {JITSI_ISS, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable";
|
||||
@ -294,6 +300,7 @@ export class SocketManager implements ZoneEventListener {
|
||||
throw 'reported socket user not found';
|
||||
}
|
||||
//TODO report user on admin application
|
||||
//todo: move to back because this fail if the reported player is in another pusher.
|
||||
await adminApi.reportPlayer(reportedSocket.userUuid, reportPlayerMessage.getReportcomment(), client.userUuid, client.roomId.split('/')[2])
|
||||
} catch (e) {
|
||||
console.error('An error occurred on "handleReportMessage"');
|
||||
@ -597,6 +604,13 @@ export class SocketManager implements ZoneEventListener {
|
||||
|
||||
client.backConnection.write(pusherToBackMessage);
|
||||
}
|
||||
|
||||
handleRequestVisitCardMessage(client: ExSocketInterface, requestVisitCardMessage: RequestVisitCardMessage) {
|
||||
const pusherToBackMessage = new PusherToBackMessage();
|
||||
pusherToBackMessage.setRequestvisitcardmessage(requestVisitCardMessage);
|
||||
|
||||
client.backConnection.write(pusherToBackMessage);
|
||||
}
|
||||
}
|
||||
|
||||
export const socketManager = new SocketManager();
|
||||
|
Loading…
Reference in New Issue
Block a user