I am developing an online game for 2 players using Vue js and Firestore.
The game starts as follows: The active user will choose a random person from online users and play games with him.
When the new game button is clicked;
- A random player will be selected from online users.
- A new game room will be created: game number and player information will be saved in the firestore database.
- For the random user selected, game number and opponent information will be matched.
- Both users will be directed to the Game page.
My codes
import db from "@/firebase/init";
export default {
name: "Home",
data() {
return {
activeUsers: [],
currentUser: null,
opponent: null,
gameNo: null
};
},
created() {
this.currentUser = this.$session.get("user");
this.createOnlineUser();
this.getOnlineUsers();
},
methods: {
createOnlineUser() {
let ref = db.collection("game_users").doc(this.currentUser.email);
ref.get().then(doc => {
if (!doc.exists) {
ref.set({
user_id: this.currentUser.id,
username: this.currentUser.username,
is_play: false
});
}
});
},
getOnlineUsers() {
db.collection("game_users")
.where("is_play", "==", false)
.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
let doc = change.doc;
var user = {
user_id: doc.data().user_id,
username: doc.data().username,
email: doc.id
};
if (change.type === "added") {
if (doc.data().user_id != this.currentUser.id) {
let userIndex = this.activeUsers.findIndex(
element => element.user_id == user.user_id
);
if (userIndex === -1) {
this.activeUsers.push(user);
}
}
} else if (change.type === "removed") {
this.activeUsers = this.activeUsers.filter(element => {
return element.user_id != user.user_id;
});
}
});
});
},
newGame() {
this.opponent = this.randomUser();
this.createGameRoom();
},
createGameRoom() {
this.gameNo = Date.now();
db.collection("game_rooms")
.add({
gameNo: this.gameNo,
players: [this.currentUser.email, this.opponent.email],
})
.then(() => {
this.addGameNoSession();
})
.then(() => {
this.createGameAndOpponent();
});
},
createGameAndOpponent() {
db.collection("game_rooms").onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
if (change.type == "added") {
let doc = change.doc;
let players = doc.data().players;
let currentUserIndex = players.indexOf(this.currentUser.email);
if (currentUserIndex != -1) {
if (!this.$session.exists("gameNo")) {
this.gameNo = doc.data().gameNo;
this.addGameNoSession();
}
}
this.addOpponentSession(currentUserIndex, players);
this.$router.push({ name: "Game" });
}
});
});
},
addGameNoSession() {
this.$session.set("gameNo", this.gameNo);
},
randomUser() {
var item = this.activeUsers[
Math.floor(Math.random() * this.activeUsers.length)
];
return item;
},
addOpponentSession(currentUserIndex, players) {
let opponent = null,
opponentIndex = null;
if (currentUserIndex == 0) {
opponentIndex = 1;
} else {
opponentIndex = 0;
}
opponent = this.activeUsers.find(
user => user.email === players[opponentIndex]
);
this.$session.set("opponent", opponent);
},
}
};
<template>
<div id="home">
<Navbar />
<div class="home container">
<div class="card">
<a @click="newGame"> New Game
</a>
</div>
</div>
</div>
</template>
Vue Router - index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Game from '../views/Game.vue'
import Login from '../views/auth/Login.vue'
import Signup from '../views/auth/Signup.vue'
Vue.use(VueRouter)
const routes = [{
path: '/',
name: 'Home',
component: Home,
meta: {
requiresAuth: true
}
}, {
path: '/game',
name: 'Game',
component: Game,
meta: {
requiresAuth: true
}
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/signup',
name: 'Signup',
component: Signup
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
router.beforeEach((to, from, next) => {
if (to.matched.some(rec => rec.meta.requiresAuth)) {
if (Vue.prototype.$session.exists('user')) {
next()
} else {
next({ name: 'Login' })
}
} else {
if (Vue.prototype.$session.exists('user')) {
if (to.name == "Login" || to.name == "Signup") {
next({ name: 'Home' })
}
}
next()
}
})
export default router
I'm checking inside the createGameAndOpponent function:
If there is a change in "game_rooms" or a new game has been created, find the opponent from online users and redirect both users to the game page. But there is an error during the redirection here. The page is not redirected and gives the following error.
But I get the error below
"Uncaught (in promise)"
Error Detail:
vue-router.esm.js:2086 Uncaught (in promise) NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated", message: "Navigating to current location ("/game") is not allowed", stack: "Error↵ at new NavigationDuplicated (webpack-int…c/views/Home.vue?vue&type=script&lang=js&:175:31)"}_name: "NavigationDuplicated"name: "NavigationDuplicated"message: "Navigating to current location ("/game") is not allowed"stack: "Error↵ at new NavigationDuplicated (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2009:14)↵ at HTML5History.confirmTransition (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2125:18)↵ at HTML5History.transitionTo (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2069:8)↵ at HTML5History.push (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2400:10)↵ at eval (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2812:22)↵ at new Promise ()↵ at VueRouter.push (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2811:12)↵ at eval (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/Home.vue?vue&type=script&lang=js&:204:26)↵ at Array.forEach ()↵ at Object.eval [as next] (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/Home.vue?vue&type=script&lang=js&:175:31)"proto: Error
eval @ vue-router.esm.js:2086
abort @ vue-router.esm.js:2117
confirmTransition @ vue-router.esm.js:2125
transitionTo @ vue-router.esm.js:2069
push @ vue-router.esm.js:2400
eval @ vue-router.esm.js:2812
push @ vue-router.esm.js:2811
eval @ cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/Home.vue?vue&type=script&lang=js&:204
eval @ cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/Home.vue?vue&type=script&lang=js&:175
next @ index.cjs.js:22426
eval @ index.cjs.js:20155
setTimeout (async)
AsyncObserver.scheduleEvent @ index.cjs.js:20153
AsyncObserver.next @ index.cjs.js:20142
QueryListener.raiseInitialEvent @ index.cjs.js:17098
QueryListener.onViewSnapshot @ index.cjs.js:17033
EventManager.listen @ index.cjs.js:16889
eval @ index.cjs.js:20004
eval @ index.cjs.js:1698
Promise.then (async)
AsyncQueue.enqueueInternal @ index.cjs.js:1696
AsyncQueue.enqueue @ index.cjs.js:1692
AsyncQueue.enqueueAndForget @ index.cjs.js:1639
FirestoreClient.listen @ index.cjs.js:20003
Query.onSnapshotInternal @ index.cjs.js:22432
Query.onSnapshot @ index.cjs.js:22413
oyunVeRakipTanimla @ cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/Home.vue?vue&type=script&lang=js&:174
eval @ cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/Home.vue?vue&type=script&lang=js&:155
Promise.then (async)
createGameRoom @ cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/Home.vue?vue&type=script&lang=js&:154
newGame @ cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/Home.vue?vue&type=script&lang=js&:133
invokeWithErrorHandling @ vue.runtime.esm.js:1853
invoker @ vue.runtime.esm.js:2178
original._wrapper @ vue.runtime.esm.js:6907
Where am I making mistakes?
What method should I follow to direct both users to the game page in real time?