Feat: Added CHANGE_USERNAME server command, registration and documentation
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.change_username.ChangeUsernameHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.change_username.ChangeUsernameParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.change_username.ChangeUsernameRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameRequest;
|
||||
@@ -168,6 +171,15 @@ public class ServerApp {
|
||||
commandRouter.register(
|
||||
LoginRequest.class, new LoginHandler(responseDispatcher, userRegistry));
|
||||
|
||||
parserDispatcher.register("CHANGE_USERNAME", new ChangeUsernameParser());
|
||||
commandRouter.register(
|
||||
ChangeUsernameRequest.class,
|
||||
new ChangeUsernameHandler(
|
||||
responseDispatcher,
|
||||
userRegistry,
|
||||
context.lobbyManager(),
|
||||
context.sessionManager()));
|
||||
|
||||
parserDispatcher.register("LOGOUT", new LogoutParser());
|
||||
commandRouter.register(
|
||||
LogoutRequest.class, new LogoutHandler(responseDispatcher, userRegistry));
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.change_username;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.User;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBodyBuilder;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** Handles CHANGE_USERNAME requests for already logged-in users. */
|
||||
public class ChangeUsernameHandler extends CommandHandler<ChangeUsernameRequest> {
|
||||
private static final Pattern VALID_USERNAME = Pattern.compile("[a-zA-Z0-9_-]+");
|
||||
private final UserRegistry userRegistry;
|
||||
private final LobbyManager lobbyManager;
|
||||
private final SessionManager sessionManager;
|
||||
|
||||
/**
|
||||
* @param responseDispatcher dispatcher used for responses
|
||||
* @param userRegistry registry containing all users
|
||||
* @param lobbyManager lobby manager used to keep lobby/game mappings in sync
|
||||
* @param sessionManager session manager used to broadcast rename events
|
||||
*/
|
||||
public ChangeUsernameHandler(
|
||||
ResponseDispatcher responseDispatcher,
|
||||
UserRegistry userRegistry,
|
||||
LobbyManager lobbyManager,
|
||||
SessionManager sessionManager) {
|
||||
super(responseDispatcher);
|
||||
this.userRegistry = userRegistry;
|
||||
this.lobbyManager = lobbyManager;
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChangeUsernameRequest request) {
|
||||
Optional<User> user = userRegistry.getBySessionId(request.getSessionId());
|
||||
if (user.isEmpty()) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(),
|
||||
"USER_NOT_LOGGED_IN",
|
||||
"This session is not associated with an active user."));
|
||||
return;
|
||||
}
|
||||
|
||||
String newUsername = request.getUsername() == null ? "" : request.getUsername().trim();
|
||||
if (newUsername.isEmpty() || !VALID_USERNAME.matcher(newUsername).matches()) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(),
|
||||
"INVALID_USERNAME",
|
||||
"Only letters, numbers, '_' and '-' are allowed."));
|
||||
return;
|
||||
}
|
||||
|
||||
User currentUser = user.get();
|
||||
String oldUsername = currentUser.getName();
|
||||
boolean changed = userRegistry.changeUsername(currentUser.getId(), newUsername);
|
||||
if (!changed) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(),
|
||||
"USERNAME_TAKEN",
|
||||
"The requested username is already taken."));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean lobbySynced =
|
||||
lobbyManager == null || lobbyManager.renamePlayer(oldUsername, newUsername);
|
||||
if (!lobbySynced) {
|
||||
userRegistry.changeUsername(currentUser.getId(), oldUsername);
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(),
|
||||
"RENAME_CONFLICT",
|
||||
"Could not update username in current lobby/game state."));
|
||||
return;
|
||||
}
|
||||
|
||||
responseDispatcher.dispatch(
|
||||
new ChangeUsernameResponse(
|
||||
request.getContext(), currentUser.getName(), currentUser.getId()));
|
||||
|
||||
broadcastUsernameChanged(oldUsername, currentUser.getName());
|
||||
}
|
||||
|
||||
private void broadcastUsernameChanged(String oldUsername, String newUsername) {
|
||||
if (sessionManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Session session : sessionManager.getAllSessions()) {
|
||||
RequestContext ctx = new RequestContext(session.getId(), 0);
|
||||
SuccessResponse ev =
|
||||
new SuccessResponse(
|
||||
ctx,
|
||||
new ResponseBodyBuilder()
|
||||
.param("EVENT", "USERNAME_CHANGED")
|
||||
.param("OLD_USERNAME", oldUsername)
|
||||
.param("NEW_USERNAME", newUsername)
|
||||
.build()) {};
|
||||
responseDispatcher.dispatch(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.change_username;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.RequestParameterAccessor;
|
||||
|
||||
/** Parses CHANGE_USERNAME requests. */
|
||||
public class ChangeUsernameParser implements CommandParser<ChangeUsernameRequest> {
|
||||
@Override
|
||||
public ChangeUsernameRequest parse(PrimitiveRequest primitiveRequest) {
|
||||
RequestParameterAccessor accessor =
|
||||
new RequestParameterAccessor(primitiveRequest.parameters());
|
||||
return new ChangeUsernameRequest(primitiveRequest.context(), accessor.require("USERNAME"));
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.change_username;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
|
||||
/** Request used to change the username of the current session user. */
|
||||
public class ChangeUsernameRequest extends Request {
|
||||
private final String username;
|
||||
|
||||
/**
|
||||
* @param context request context for responses
|
||||
* @param username desired new username
|
||||
*/
|
||||
public ChangeUsernameRequest(RequestContext context, String username) {
|
||||
super(context);
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return desired new username
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.change_username;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserId;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBodyBuilder;
|
||||
|
||||
/** Response for successful username changes. */
|
||||
public class ChangeUsernameResponse extends SuccessResponse {
|
||||
/**
|
||||
* @param context request context
|
||||
* @param username current username after the rename operation
|
||||
* @param id user id of renamed user
|
||||
*/
|
||||
public ChangeUsernameResponse(RequestContext context, String username, UserId id) {
|
||||
super(
|
||||
context,
|
||||
new ResponseBodyBuilder()
|
||||
.param("USERNAME", username)
|
||||
.param("ID", id.value())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user