multi redux store on node socket server - node.js

I'm currently learning react and redux and I am making a practical project in order to be face of some issues.
The project is a multiplayer labyrinth so I decided that using a socket.io server and also that player will be limited in a labyrinth game context, so I am using room (io namespace).
All the game context is store in redux on the node server and is connect to the clients UI.
because of that I have multi-Rooms server I want a different store for each room/game. In absolute I can combine reducers but that mean lot of performance lost and need a dark architecture of action and store.
the problem is that I don't know how create multi-store on node.js
currently the server work in this case ->
on client connect -> if the current room is full generate a new game else assign on current room
public generate = (server:SocketIO.Server) =>{
let newIDGenetator = new Idgenerator();
let store = redux.createStore(labActionReducer);
let room = server.of('/room'+this.labs.length);
let socketHandler = new SocketHandler(room,store,newIDGenetator);
this.labs.push(socketHandler);
this is the function call for generate a new game:
-> create idgenerator which is just an incr who's return is value cast in string
-> create a new store
-> create a io namespace
-> create a new socketHandler which implements:
room.on('connect' , (client,store) =>
client.on('foo',()=>
store.dispatch({type:bar});
)
)
and listen store in order to send the new state to each clients of the handling room
the first room work well but when a second root is create the client show the data of the first room state when I attempt to get the initial state
The client is connect on the good namespace,
So I suppose that createStore do not create a new store. Could you help me to create a new store independent of the first? if need more details I can give you the GitHub link of the project.
thank you for reading
edit: socketHandler code:
export class SocketHandler{
private playersId = {};
private store:Store<Lab,LabActionTypes>;
constructor(server:Namespace,store:Store<Lab,LabActionTypes>,idgenerator:Idgenerator){
this.store = store;
store.subscribe(()=>{
console.log(this.store.getState());
for(let playerId in this.playersId){
let newState = this.store.getState() //utiliser reducer_relatif(state,playerId)
this.playersId[playerId].emit('gridChanged',newState);
}
})
server.on('connect',(client)=>{
let newID = idgenerator.generetaId()
this.playersId[newID]=client;
store.dispatch(action.NewPlayer(newID));
NewClient(client,store,this);
});
}
public getPlayerBySocket = (socket:SocketIO.Socket)=>{
for(let playerId in this.playersId){
if(this.playersId[playerId]===socket){
return playerId;
}
}
}
}
export const NewClient = (client: SocketIO.Socket,store:redux.Store<Lab,LabActionTypes>,socketHandler:SocketHandler) => {
client.on('up',()=>{
let player = socketHandler.getPlayerBySocket(client);
store.dispatch(action.PlayerMooveUp(player,store));
console.log(JSON.stringify(store.getState()));
})
client.on('down',()=>{
let player = socketHandler.getPlayerBySocket(client);
store.dispatch(action.PlayerMooveDown(player,store));
})
client.on('left',()=>{
let player = socketHandler.getPlayerBySocket(client);
store.dispatch(action.PlayerMooveLeft(player,store));
})
client.on('right',()=>{
let player = socketHandler.getPlayerBySocket(client);
store.dispatch(action.PlayerMooveRight(player,store));
})

Related

SwiftUI: How to add CoreData record from Siri Intent

I am trying to create an Intent that saves a record to a CoreData database. The record will be created if I run the code from the main app, but not in the Intent.
Here is the code:
import Intents
import CoreData
import SwiftUI
let persistenceController = PersistenceController.shared
class IntentHandler: INExtension, DiaryIntentHandling
{
var moc = PersistenceController.shared.context
override func handler(for intent: INIntent) -> Any?
{
guard intent is DiaryIntent else
{
fatalError("Unknwonwn intent type: \(intent)")
}
return self
}
func handle(intent: DiaryIntent, completion: #escaping (DiaryIntentResponse) -> Void)
{
guard let message = message
else
{
completion(DiaryIntentResponse(code: .failure, userActivity: nil))
return
}
completion(DiaryIntentResponse.success(message: message))
let context = PersistenceController.shared.container.viewContext
let myRecord = MyRecord(context: context)
myRecord.timestamp = Date()
myRecord.message = message
do {
try context.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
func resolveMessage(for intent: DiaryIntent, with completion: #escaping (INStringResolutionResult) -> Void)
{
if let message = intent.message
{
completion(INStringResolutionResult.success(with: message))
}
else
{
completion(INStringResolutionResult.needsValue())
}
}
public func confirm(intent: DiaryIntent, completion: #escaping (DiaryIntentResponse) -> Void) {
completion(DiaryIntentResponse(code: .ready, userActivity: nil))
}
}
Do I need to share access to the CoreData database? How do I create the record?
App extensions work like separate apps, so you need to set up an app "group" to share data between them. It gives you a directory that's not part of your app's sandbox that your app and your app extensions can share. Using one requires some setup work:
Turn on app groups by adding the group entitlement. Apple has some documentation on this. I also have a somewhat old blog post that's still accurate as far as setting up the group.
Set up your persistent container use the group directory for Core Data. Normally it saves data in your app's sandbox, but you can tell it to use the app group directory. To do that,
Get a file URL for the directory using FileManager's function containerURL(forSecurityApplicationGroupIdentifier:). The argument is the same as your app group identifier.
Make sure this directory exists! It doesn't get created automatically. Use FileManager.default.fileExists(atPath:) to check if it exists, and if not, use FileManager.default.createDirectory(at:withIntermediateDirectories:attributes:) to create it.
Use a NSPersistentStoreDescription to tell your persistent container to use that URL for Core Data. That would be something like
let persistentContainer = NSPersistentContainer(name: containerName)
let persistentStoreDescription = NSPersistentStoreDescription(url: persistentStoreUrl)
persistentStoreDescription.type = NSSQLiteStoreType
persistentContainer.persistentStoreDescriptions = [ persistentStoreDescription ]
After the previous step, the persistent container won't be able to find any data that's currently in Core Data. So:
If your app has not already been released, delete it from your test devices and simulators and then rebuild. You'll get a new persistent store in the app group directory.
If your app has already been released, add code to copy the persistent store from the current location to the new location. Do this before loading the persistent store, and only do it if the copy in the app group doesn't already exist (so you don't re-copy old data). The best way to do that is with the migratePersistentStore(_:to:options:withType:) function from NSPersistentStoreCoordinator. Don't just copy your SQLite file over, because that won't include all the data.

how do i use bot.on features inside a module.exports file

I developed an algorithm that when u type in discord server's text channel : "-nuke #tag" that member will get a "Muted" role and his id will be added to an array. If he leaves the server and joins back, the bot will compare the new member's id with all the ids in the array and if they are matching it will automatically give that person the "Muted" role. The problem is, the bot.on doesn't seem to work inside something else than index.js. I dont really want to go inside all the event handlers and stuff, just to get this one working nuke.js
This answer is in reference to a comment I made. This will show you how to create and access a global map.
To start you need to extend the client class by the map you want to use. You do that like this.
const { Client } = require('discord.js');
module.exports = class extends Client {
constructor(config) {
super({});
this.muteIDs = new Map(); // you can name it whatever you like
this.config = config;
}
};
You should do that in a separate file. Lets call that file clientextend.js. Now you can create your bot client like you usually would, however you need to require the file that you just created. This example has both the file in which you extend the client and the file you create the client in, in the same directory. Note: In your example you have bot instead of client.
const Client = require('./clientextend.js')
const client = new Client();
You now have access to the map you created anywhere you have your client available to you. You can access it like this.
muteIDs = client.muteIDs;

How to start learning socket programming using node.js?

I am interested to learn socket programming and to create a chat application using Node.js.
There are two different things exist
1) net.Socket class exposed by built-in net module
2) socket.io package. (Node.js implementation)
Which one of them can be referred?
If socket.io is a wrapper for net.Socket, why it's not imported by socket.io?
I am not that familiar with net.socket but if you are new to sockets in node, I would recommend socket.io. There are a lot more resources available and I would say it is a lot more commonly used.
Practicing by building a chat application is a good use case.
You can even follow socket.io's toturial here
I just finished writing an app using socket.io, nodejs (express) and mongodb. For front end, I used ios 11.
I have to say that it is very easy to use these technologies given all the knowledge out there.
The basic socket.io flow works with duplex communication between front end (client) and backend (client).
1. A client "emits" an event to server
2. Server acts upon it can can choose to emit an even back.
3. Server receives the event and acts on it..
For example:
In my ios app:
func sendMessageToTheRoom(message: String, name: String, roomName: String) {
socket.emit("messageToRoom", message, name, roomName)
}
Then client responds like so:
clientSocket.on('messageToRoom', function(message, name, roomName) {
console.log(message);
console.log(name);
console.log(roomName);
io.to(roomName).emit('messageReceived', message, name, roomName);
});
And finally, the receiver clients act upon server's emit action like so:
socket.on("messageReceived") { (dataArray, socketAck) -> Void in
var messageDictionary = [String: AnyObject]()
messageDictionary["message"] = dataArray[0] as! String as AnyObject
messageDictionary["name"] = dataArray[1] as! String as AnyObject
messageDictionary["roomName"] = dataArray[2] as! String as AnyObject
print(messageDictionary["message"] as! String)
if messageDictionary["name"] as? String != self.nickName {
DispatchQueue.main.async {
let message = Message()
message.text = messageDictionary["message"] as? String
message.isSender = false
self.messages?.append(message)
self.chatCollectionView.reloadData()
}
}
NotificationCenter.default.post(name: Notifications.newMessageSent.name, object: nil)
}
But this is just an example.

How to share dynamic objects across workers?

I'm trying to make a game, which works on rooms, lobby and such (imagine the chat app, except with additional checks/information storing).
Let's say, I have a module room.js
var EventEmitter = require('events');
class Room extends EventEmitter {
constructor (id, name) {
super();
this.id = id;
this.name = name;
this.users = [];
}
}
Room.prototype.addUser = function (user) {
if(this.users.indexOf(user) === -1) {
this.users.push(user);
this.emit('user_joined', user);
} else {
/* error handling */
}
};
module.exports = {
Room: Room,
byId: function (id) {
// where should I look up?
}
};
How can I get exactly this object (with events)? How can I access events emitted by this object?
In a single instance of node, I would do something like:
var rooms = [];
var room = new Room(1234, 'test room');
room.on('user_joined', console.log);
rooms.push(room);
Also, I don't quite understood how Redis is actually helping (is it replacement of EventEmitter?)
Regards.
EDIT: Would accept PM2 solutions too.
Instead of handling rooms in Node, you can replace them with channels in Redis).
When a new client wants to join in a room, the NodeJS app returns it the ID of this given room (that is to say the name of the channel), then the client suscribes to the selected room (your client is directly connected to Redis.
You can use a Redis Set to manage the list of rooms.
In this scenario, you don't need any event emitter, and your node servers are stateless.
Otherwise, it would mean Redis would be exposed on the Internet (assuming your game is public), so you must activate Redis authentication. A major problem with this solution is that you have to give the server password to all clients, so it's definitely unsecure.
Moreover, Redis' performances allow brute force attacks so exposing it on Internet is not recommended. That's why I think all communications should go through a Node instance, even if Redis is used as a backend.
To solve this, you can use socket.io to open sockets between Node and your clients, and make the Node instances (not the client) subscribe to the Redis channel. When a message is published by Redis, send it to the client through the socket. And add a layer of authentication to ensure only valid clients connect to a given channel.
Event emitter is not required. It's the Redis client which will be an event emitter (like in this example based on ioRedis)

Multiplayer game server with multiple node

Say you're making a Counter-Strike server. We have a single lobby and multiple rooms in which game sessions occur.
I.E I have 3 node server, nginx load balancer, redis.
SCHEMA
I'm using socket.io, with redis adapter, So I can emit messages to every clients on any server.
I can't understand one thing. For example I have something like this:
var currentGames = {};
socket.on('createGame', function(gameName){
var game = new Game();
game.addPlayer(socket.id);
currentGames.push();
// ... Send to all clients, that game created and they can join
});
socket.on('joinGame', function(gameName){
currentGames[gameName].addPlayer(socket.id);
// ... Send to all players of this game, that player joined
});
socket.on('walk', function(gameName, coordinates){
currentGames[socket.id].walk(player, coordinates);
// ... Get all players positions and send to clients
});
class Game
{
gameName;
players = {};
score;
constructor(name){
this.gameName = name;
}
addPlayer(player){
players[name] = new Player(player);
}
walk(id, coordinates){
this.players[id].updatePosition(coordinates);
}
}
class player
{
id;
life = 100;
coordinates = {0,0,0};
kills;
death;
constructor(id){
this.id = id;
}
updatePosition(coords){
this.coordinates = coords;
}
}
I've just written this code, for example.
let, user_1 is on node-1 and user-2 is on node-2.
If user_1 creates game, instance will be created and saved on node-1.
so when user_2 receives information about game, which was created and he clicks a join button,
client will send request to server and on node-2 there won't be the game instance, which user_1 created.
I hope, you will understand, what I'm talking (my English isn't good).
I guess, that currentGames object of game instances must be a global, for all nodes. But I don't know how.
Should I use redis or mongo or something else, for storing game information, instead of variable?
Am I going with wrong way?
U should sync Game State in Redis, room and game instance, too.
I have some note :
+ Entity State - manage Game State, store and set, get Game information to Redis
+ Client State - each connection client have Client State and Sync with Entity State, each tick in Game loop, u check difference beetween Entity State and Client State and send it to Client and update Game Client

Resources