Sending "secret" data to individual players in Lance Game - javascript

I'm wondering if there is an easy way in lance-gg to send player specific data to each player only, rather than emitting all data to all players.
I wish to have create a poker game, and don't want the data around what each player is holding broadcast to all players and instead only have each player receive information regarding their own cards.
Is this easily achievable between the current GameEngine and ServerEngine?
Over the course of the game the following steps will need to occur:
"deal" cards to each player (emit cards to each player individually, and also broadcast an event to indicate the other clients should animate the player being dealt cards receiving them)
store and hold the dealt cards outside the other data to be updated
retrieve player cards should the player be disconnected and then reconnect during the hand
"reveal" cards (broadcast any revealed cards like the flop and the showdown cards to all players)
Player's cards also need to be stored on the server but not re-broadcast with each step.

There is a low-level networking layer which can be used for client-server communication in Lance.
For example, if the server wants to send the event shakeItUp with data shakeData = { ... } to all clients, the game's serverEngine would call:
this.io.sockets.emit('shakeItUp', shakeData);
To send events and data to specific players, the serverEngine class can do
for (let socketId of Object.keys(this.connectedPlayers)) {
let player = this.connectedPlayers[socketId];
let playerId = player.socket.playerId;
let message = `hello player ${playerId}`;
this.connectedPlayers[socketId].socket.emit('secret', message);
}
The client listens to messages from the ClientEngine sub-class, after the connection is established:
// extend ClientEngine connect to add own events
connect() {
return super.connect().then(() => {
this.socket.on('secret', (e) => {
console.log(`my secret: ${e}`);
});
});
}

Related

Discord.js - Secretly getting user input

I am making a 2-player rock-paper-scissors game using Discord.js.
Sadly the Discord API is really slow and afaik doesn't provide any type of middleware. When someone chooses their shape, the other person sees the reaction (or chat message) for quite a while until the bot deletes it, therefore ruining the whole game.
The only way of secretly getting an input I could think of, was sending the user a message in private chat, to which he can react. But having to switch from the server to private chat and then back to the server just makes the game unplayable in my opinion. It's just too much work for the user, compared to simply clicking a reaction.
Another option would be sending a message in the chat, which only a specific user can see. It could say something like "1 = Scissors, 2 = Rock, 3 = Paper". (The mapping would be randomized for each player). The user then picks the corresponding reaction from the options 1, 2 and 3.
But it seems, Discord does not allow to send a message in chat, which only a specific user can see. Or is there a way?
And is there any way of secetly getting user-input without the user having to switch chats?
Does the API maybe provide any kind of middle-ware for messages or reactions which I have overlooked?
Discord does not allow to send a message in chat, which only a specific user can see. Or is there a way?
No, there isn't. Discord API doesn't allow you to specify users that can see a specific guild message.
And is there any way of secetly getting user-input without the user having to switch chats?
There definitely is!
You could use a fairly new feature, buttons. Below is an example code, you can use to base your game on. Things left to implement are:
Gamestate, e.g. who is on turn, who has how much points, etc.
Update gamestates (identify gamestate by id?) in the interactionCreate event's callback.
Showing the gamestate to the players, e.g. updating the original message.
Don't allow players to modify the gamestate of other playing pairs.
Let the user specify an opponent in !createGame command.
The actual game logic (determining who won, who gets a point, etc.)
That is all I can think of for now. Take those more of a suggestions than requirements. There is no boundary to ones creativity.
// ... Some object to store the gamestates in? ...
client.on("messageCreate", async (message) => {
// Don't reply to bots
if (message.author.bot) return;
// Basic command handler, just for showcasing the buttons
if (message.content.startsWith("!createGame")) {
// ... Probably some argument handling for the opponent (e.g. a mention) ...
// Create an action row component with buttons attached to it
const actionRow = new Discord.MessageActionRow()
.addComponents(
[["Rock", "🗿"], ["Paper", "🧻"], ["Scissors", "✂️"]].map((buttonProperties) => {
return new Discord.MessageButton()
.setStyle("PRIMARY")
.setLabel(buttonProperties[0])
.setEmoji(buttonProperties[1])
.setCustomId(`rpsgame_${buttonProperties[0].toLowerCase()}`);
})
);
// Send the game message/playground
message.channel.send({
content: "Rock, Paper and Scissors: The game!",
components: [actionRow]
});
}
});
To handle the button clicks, we use the interactionCreate event.
client.on("interactionCreate", (interaction) => {
// If the interaction is a button
if (interaction.isButton()) {
// If the button belongs to our game
if (interaction.customId.startsWith("rpsgame")) {
// Log what the user has selected to console, as a test
console.log(`A user '${interaction.member.user.tag}' selected ${interaction.component.emoji.name}.`);
// Don't forget to reply to an interaction,
// otherwise an error will be shown on discord.
interaction.update("Update GameState...");
}
}
});
No one will see what the other users have selected, unless you want them to.
Using discord.js ^13.0.1. If you are on v12, there is a nice package called discord-buttons.

Having trouble emitting to multiple users with socket.io

I'm working on an educational project - online checkers (draughts) game and i'm still not feeling comfortable enough with socket.io.
What i'm trying to do is this - When a player wants to invite another player to a game room to play, using the socket.io I want to send both of the players from the lobby to the game room. Only they get sent to the game room, meaning that even if there are hundreds of players inside the lobby, only the 2 players that wants to play get redirected to the game room.
When player 1 clicks on a button, I emit his details and player's 2 details (The player that player 1 has chosen to play against) to the server as seen below:
socket.emit("SendPlayersToRoom", { player1, player2}, socket.id)
The socket.id argument is the socket.id of player 1. player 2 is an object that already includes his socket id at player2.id.
Next, I receive the information at the server side and then I try to emit this only to players 1 and 2, as seen below:
socket.on("SendPlayersToRoom", ({player1, player2}, id) => {
io.to(id).to(player2.id).emit("sendToRoom");
})
Finally, I receive the info at the client side where it should redirect both players to a new room where they can play:
socket.on("sendToRoom", () => {
location.href = `/game-room?p1=${player1.username}&p2=${player2.username}`
})
What actually happens with this code is that player 1 (The one who initiated the game invitation) gets sent to the room without player 2, player 2 stays at the lobby as if nothing happened.
What am I missing here? What am I doing wrong?
You have a unique id for your room. Make sure both sockets join it.
socket.join(id) - use this for both your sockets.

Passing new DOM Elements via Socketio

How can i pass DOM Elements server-side via socketio to display new message content.
// Client
// Pass DOM element as string
function elemToString(elem){
return elem.outerHTML
}
window.addEventListener("keyup", sendMessage, false);
function sendMessage(key) {
if (key.keyCode === 13) {
if (chatbox.value !== "") {
const child = document.createElement("div");
child.textContent = `${chatbox.value}`
child.className = "messages"
chat.appendChild(child);
socket.emit('chatMsg', elemToString(child))
chatbox.value = "";
chatbox.style.display = "none";
setTimeout(()=>{
child.remove()
},4000)
}
else chatbox.style.display = "none";
}
}
// User Class
class User {
constructor(name, x, y) {
this.alive = false
this.name = name
this.cells = []
this.room = 'none'
this.message = []
}
update(cells) {
this.cells = cells
}
}
module.exports = User
// Server
socket.on('chatMsg', elem => {
user.message.push(elem)
})
I have a hotkey which sends new divs which display message content. I want to pass the data server-side via socket.io so all users can see new message content instead of just the individual client.
The first question I would ask is whether you really need to pass around HTML or DOM objects? Based on the above snippet, it appears that sending the messages as strings (without any HTML markup, just the message body) is by far the easiest way for both the server and the chat client(s). As to sending the message to all users in the chat room, this feature is already built into socket.io (see https://socket.io/docs/rooms-and-namespaces/) and unless you need to implement custom logging or filtering, you could just use it out of the box
There are several ways you could go about building this chat server:
Simple / POC approach
If you just want to have a quick prototype and see if it does what you want it to do, and you are not too concerned with auditing, logging, filtering etc., then:
When the client connects, it "joins" a socket.io "room" (the name of the room could match the room on the client or you may choose to format/hash it etc.)
All chat room clients send events to that same room and listen to the events emitted by that room. The socket.io backend takes care of re-broadcasting the events, so you would not need to do anything
To send message to the room, the "sender" client would broadcast a well known event, something like { message: '<<message text>>' }. All clients listen to events in the room, and once a new event arrives they create a new HTML element (say, a <div>) with that message as content and append it to the chat window
This should cover the basic chat room scenario
A bit more involved chat server
In the real-world implementations, we typically want to have a bit more control over what events can and cannot be broadcasted (for example, we want to have the ability to silence a chatter or filter profanity/abusive language etc.) In this case, we cannot simply use blind broadcast, so typically the client would use two socket.io channels - a "room" for incoming messages and a direct channel for outgoing.
In this scenario, the server acts as a bridge - it listens to the incoming messages, does all the necessary filtering, preprocessing, sanitizes the messages to prevent injections etc., and then broadcasts the resulting message in the room. In this scenario:
All chat clients subscribe to the same "incoming message" room, just like in the previous scenario
When the chatter needs to send a message (say, they hit Enter), the client socket emits an event to the server and tells it which room this message is intended for (e.g. { incoming_message: 'xyz', user: 'username', room: 'room_21' }
The server receives the message and processes it (for example, checks the username against the list of banned users), then broadcasts the processed message in the room specified in the original message. The other chat participants receive the message and render it
In this scenario, you can keep the history of every message sent by a particular user (much like you do in your code snippet)
This link (https://socket.io/docs/#Sending-and-getting-data-acknowledgements) has a few examples on sending and broadcasting arbitrary JSON messages and also shows working with channels etc.
Hope that helps and good luck with your chat server!

Socket.io room feature to create game tables on demand

I am using JS and socket.io to make a multiplayer game.
The game player limit is ranging from 2-5.
What I want to achieve is :
1: On the client browser there is a dropdown list displaying all the game table names and its max player limit. A player can join any room by click on it.
2: Once the table player limit is reached, the room cannot be joined again.
3:players in each room can play their own game, chat in their own channel without being distracted.
In my current design there is a static room object and in the room project there is an array of pre-created tables object. The table object has many of the properties and methods that a game would need.
Server side room and table object:
function Room(name) {
this.players = [];
this.tables = [];
};
function Table(tableID){
this.id = tableID;
this.players = [];
this.maxPlayerLimit = 4;
};
I can pre-created amount number of tables and push these tables object to room's tables array.
Room.prototype.createTables = function(amount) {
var tableList = [];
for(var i = 0; i < amount; i++){
var table = new Table(i+1);
table.setName("Table" + (i + 1));
tableList.push(table);
}
return tableList;
};
client side when they click on the dropdown:
The drop down looks like https://jsfiddle.net/1oyy98uo/
socket.emit("connectToTable", {tableID:TableID,tableName: tableName,limit:limit}); //these info is based on their table selection in the dropdown
When a player connect to server I push them to the static room player array.
when they choose a table, I do
socket.on('connectToTable',function(data) {
var player = room.player(socket.id)// (room method to get the socket player)
var table = room.table(data.tableID); //(room method to get the table)
table.Name(data.tableName)//(table method to set name)
table.maxPlayerLimit = Number(data.limit)
if (tableisAvailable){
table.join(player)//table method to add player to its list
if(table.players.length===maxPlayerlimit){
init the game
}else{
client.emit("table is full")//then the client would know to go elsewhere
}
}
})
My design fulfills all the goals. but it is done in such a hard-coded way. The pre-created table might be too many or too little. The only way to solve this with my current design is to create more rooms than expected.
I am not sure if I should still stick with my current design or socket.io room feature can provide a better solution. (creating game tables on demand)
But there is a flaw in the on-demand scenario:
players make requests to join a room(table), once the requests reach to a certain limit (like 4) the server will join these players. This way the server will only create as many rooms(tables) as necessary. But these queued-up players are random players, they may not necessarily want to play with each other. If players want to play with their friends, this will be a problem.
With the Socket.io room feature, is there a more dynamic way or better way to create rooms(in my case is game tables) with different player limit ON DEMAND?
socket.io rooms are completely dynamic. You don't "create a room". Instead, you just join a socket to a room and that room will be automatically created. Similarly, you don't remove removes either. When the last socket leaves a room, the room just disappears.
So, if you use socket.io rooms instead of your Tables, then their creation will be entirely on demand. You can iterate the rooms in service at any time. You can iterate the sockets in a room at any time. You can add or remove a socket from a room at any time.

AJAX: People interacting on a page in real time without a database?

Background
I am trying to design an interactive classroom type of environment. The room has different slides, a chat box and some other basic features.
My understanding
A sure way to update a page in real time for all users is for one person to persist a change via ajax to a database, then all the other users poll the server at a timed interval (one second) to check for changes... if there are their view gets updated.
My code
Each room has a unique URL... http://www.example.com/room/ajc73
Users slide through the clides using this code:
showCard();
function showCard() {
$('#card-' + (cardId)).show();
}
$('#nextCard').click(function() {
nextCard();
});
$('#previousCard').click(function() {
previousCard();
});
function nextCard() {
if ($('#card-' + (cardId + 1)).length != 0) { // if there is a next card
$('#card-' + (cardId)).hide(); // hide current card
cardId++; // increment the card id
$('#card-' + (cardId)).show(); // and show the next card
location.hash = cardId;
}
}
function previousCard() {
if (cardId != 1) { // if we are not at the first card
$('#card-' + (cardId)).hide(); // hide the current card
cardId--; // decrement the card id
$('#card-' + (cardId)).show(); // and show the previous card
location.hash = cardId;
}
}
My question
Am I required to persist data from user1 to the database in order for it to be called and displayed to user2 or is there a way to cut out the database part and push changes directly to the browser?
Go for websockets. that will be a better option. since its real-time and just a simpler logic will help you achieve the result.
If you are not sure that whether you will be able to use websockets(like if you are using shared hosting and your provider doesn't allow this or any other reason) you can go for various services like pusher(easier to understand) that will help to do your job but with some cost.
You could use sockets and just broadcast any input to every client.
Of course, the same can be done with ajax and a rest api but it'll be harder, i'll use pseudocode:
clients = {};
fn newclient() {
clients[client_id] = {
pool: [];
....
}
}
fn onNewMessage(newmessage) {
forEach(client, fn(c) {
c.pool.push(newmessage);
})
}
fn clientRequestNews() {
response = clients[client].pool;
clients[client].pool.length = 0;
return response;
}
the point here is, in server memory there would be a entry for each client, each of them has a pool, when a new message is sent to the server, it's pushed to every client's pool.
When a client ask's for news, the server will return the clients pool, after that, it'll clean the pool of that client.
With this you dont need persistence.
Use web sockets. Please see here
You need websockets, a datastructure server and a pub/serve model with events:
A hint

Categories

Resources