diff --git a/src/main/java/com/alterdekim/game/component/GameServer.java b/src/main/java/com/alterdekim/game/component/GameServer.java index 2bf889d..87eca22 100644 --- a/src/main/java/com/alterdekim/game/component/GameServer.java +++ b/src/main/java/com/alterdekim/game/component/GameServer.java @@ -169,6 +169,10 @@ public class GameServer { } this.sendResult(playerId, message.getTransactionId(), xmlMapper.writeValueAsString(locationService.getLocationById(nextLocation)).replace("<", "<")); } + case GetCatalogInfo -> { + long catalogObjectId = message.getArguments().get(0).getLong(); + // todo: implement + } case ChatMessage -> { String text = message.getArguments().get(1).toString(); sendInPlayersLocation(playerId, CommandType.ChatMessage, new ChatMessage(playerId, text)); diff --git a/src/main/java/com/alterdekim/game/component/StartUpListener.java b/src/main/java/com/alterdekim/game/component/StartUpListener.java index 342ad11..d9c184f 100644 --- a/src/main/java/com/alterdekim/game/component/StartUpListener.java +++ b/src/main/java/com/alterdekim/game/component/StartUpListener.java @@ -141,7 +141,6 @@ public class StartUpListener { public void onApplicationEvent(ContextRefreshedEvent event) { this.startTime = System.currentTimeMillis(); - // todo: compile other swf's if( userService.findByUsername(ADMIN_USERNAME) != null ) { // todo: remove hardcoded cache folder @@ -161,7 +160,6 @@ public class StartUpListener { throw new RuntimeException(e); } } - // todo: compile base swf with some changes (make smiles work) if( userService.findByUsername(ADMIN_USERNAME) != null ) return; @@ -173,8 +171,6 @@ public class StartUpListener { try { processResources(storageProperties.getLocation() + File.separator + config.getDefaultResourcesPath()); processBase(storageProperties.getLocation() + File.separator + config.getDefaultBasePath()); - // todo: implements miniquests - String miniquests = storageProperties.getLocation() + File.separator + config.getDefaultMiniquestsPath(); processCatalogs(storageProperties.getLocation() + File.separator + config.getDefaultCatalogsPath()); } catch (IOException e) { log.error("Unable to move pre-compiled data to the server's database: {}", e.getMessage()); diff --git a/src/main/java/com/alterdekim/game/component/game/UserCommandType.java b/src/main/java/com/alterdekim/game/component/game/UserCommandType.java index e7eac57..f20200b 100644 --- a/src/main/java/com/alterdekim/game/component/game/UserCommandType.java +++ b/src/main/java/com/alterdekim/game/component/game/UserCommandType.java @@ -14,6 +14,7 @@ public enum UserCommandType { UpdateUserData("_D"), GetUserLocation("_LG"), GetUserLocationById("_LGI"), + GetCatalogInfo("_GCI"), ChatMessage("_C"), Shoot("_NUS"), GetUserInfo("_UI"), diff --git a/src/main/java/com/alterdekim/game/controller/ApiController.java b/src/main/java/com/alterdekim/game/controller/ApiController.java index 5db7f2e..5ab890e 100644 --- a/src/main/java/com/alterdekim/game/controller/ApiController.java +++ b/src/main/java/com/alterdekim/game/controller/ApiController.java @@ -3,6 +3,10 @@ package com.alterdekim.game.controller; import com.alterdekim.game.component.GameServer; import com.alterdekim.game.component.NativeDao; import com.alterdekim.game.component.StartUpListener; +import com.alterdekim.game.component.game.ClubAccessType; +import com.alterdekim.game.component.game.PlayerProperties; +import com.alterdekim.game.component.game.PlayerPropertyType; +import com.alterdekim.game.component.game.RoleFlags; import com.alterdekim.game.controller.result.api.ApiResult; import com.alterdekim.game.controller.result.api.PreloaderResult; import com.alterdekim.game.controller.result.api.PromotionBannerResult; @@ -103,7 +107,8 @@ public class ApiController { Promotions("promotions", Promotion.class, StaticController.PanelSection.Preloaders), Banners("promotion_banners", PromotionBanner.class, StaticController.PanelSection.Preloaders), Locations("locations", Location.class, StaticController.PanelSection.Locations), - LocationObjects("location_objects",LocationObjectInstance .class, StaticController.PanelSection.Locations); + LocationObjects("location_objects",LocationObjectInstance .class, StaticController.PanelSection.Locations), + Users("users", User.class, StaticController.PanelSection.Users); private final String val; private final Class cl; @@ -151,6 +156,7 @@ public class ApiController { case Banners -> promotionBannerService.getAll(); case Locations -> locationService.getAll(); case LocationObjects -> locationService.getAllLocationObjects(); + case Users -> userService.getAll(); }; return ResponseEntity.ok( @@ -161,6 +167,54 @@ public class ApiController { ); } + @RequestMapping(value = "/reset_user_property", method = RequestMethod.POST) + public ResponseEntity resetUserProperty(@RequestParam("user_id") Integer userId, @RequestParam("entry_name") String entryName) { + PlayerProperties property = PlayerProperties.valueOf(entryName); + userService.removeUserProperty(userId, property); + userService.getUserInitInfoByUserId(userId); + return ResponseEntity.ok().build(); + } + + @RequestMapping(value = "/edit_user_property", method = RequestMethod.POST) + public ResponseEntity editUserProperty(@RequestParam("user_id") Integer userId, + @RequestParam("entry_name") String entryName, + @RequestParam("entry_value") String entryValue) { + PlayerProperties property = PlayerProperties.valueOf(entryName); + Object val = switch (property.getValueType()) { + case Enum -> { + if( property == PlayerProperties.RoleFlags ) { + yield RoleFlags.valueOf(entryValue); + } + yield ClubAccessType.valueOf(entryValue); + } + case Boolean -> Boolean.parseBoolean(entryValue); + default -> Integer.parseInt(entryValue); + }; + userService.pushUserProperty(userId, property, val); + return ResponseEntity.ok().build(); + } + + @RequestMapping(value = "/get_user_properties", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> getUserProperties(@RequestParam("id") Integer userId) { + List results = new LinkedList<>(); + for( PlayerProperties property : PlayerProperties.values() ) { + Object value = userService.findUserProperty(userId, property).orElse(null); + if( value == null ) continue; + results.add(new UserPropertyResult(property.getValueType(), property.name(), value)); + } + return ResponseEntity.ok(results); + } + + @AllArgsConstructor + public static class UserPropertyResult { + @JsonProperty + private PlayerPropertyType type; + @JsonProperty + private String name; + @JsonProperty + private Object value; + } + @RequestMapping(value = "/add_row", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity addSpecificRow(HttpServletRequest httpServletRequest) { if( !httpServletRequest.getParameterMap().containsKey("table") ) return ResponseEntity.badRequest().build(); diff --git a/src/main/java/com/alterdekim/game/controller/StaticController.java b/src/main/java/com/alterdekim/game/controller/StaticController.java index e2c27ee..8e602fe 100644 --- a/src/main/java/com/alterdekim/game/controller/StaticController.java +++ b/src/main/java/com/alterdekim/game/controller/StaticController.java @@ -68,6 +68,13 @@ public class StaticController { return "panel"; } + @GetMapping("/panel_user") + public String panelUser(Model model, @RequestParam(value = "id", defaultValue = "1") Integer userId) { + model.addAttribute("user_id", userId); + model.addAttribute("user_name", userService.getUsernameById(userId).get()); + return "panel_user"; + } + public enum PanelSection { Dashboard, Locations, diff --git a/src/main/java/com/alterdekim/game/controller/result/api/UserResult.java b/src/main/java/com/alterdekim/game/controller/result/api/UserResult.java new file mode 100644 index 0000000..3648903 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/api/UserResult.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.controller.result.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class UserResult { + @JsonProperty + private Integer id; + + @JsonProperty + private String username; + + @JsonProperty + private Boolean isBanned; +} diff --git a/src/main/java/com/alterdekim/game/entity/User.java b/src/main/java/com/alterdekim/game/entity/User.java index b044924..6af85e8 100644 --- a/src/main/java/com/alterdekim/game/entity/User.java +++ b/src/main/java/com/alterdekim/game/entity/User.java @@ -2,6 +2,8 @@ package com.alterdekim.game.entity; import com.alterdekim.game.component.game.ClubAccessType; import com.alterdekim.game.component.game.RoleFlags; +import com.alterdekim.game.controller.result.api.ApiResult; +import com.alterdekim.game.controller.result.api.UserResult; import com.alterdekim.game.repository.UserRepository; import com.alterdekim.game.xml.NumericBooleanSerializer; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -26,7 +28,7 @@ import java.util.List; @AllArgsConstructor @Entity @Table(name="users") -public class User { +public class User implements ApiResult { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -53,4 +55,9 @@ public class User { this.id = id; this.username = username; } + + @Override + public UserResult toAPIResult() { + return new UserResult(this.id, this.username, this.isBanned); + } } diff --git a/src/main/java/com/alterdekim/game/security/SpringSecurity.java b/src/main/java/com/alterdekim/game/security/SpringSecurity.java index c44dec1..a2d6f38 100644 --- a/src/main/java/com/alterdekim/game/security/SpringSecurity.java +++ b/src/main/java/com/alterdekim/game/security/SpringSecurity.java @@ -37,6 +37,7 @@ public class SpringSecurity { .requestMatchers("/").permitAll() .requestMatchers("/login").permitAll() .requestMatchers("/panel").hasAuthority(Role.RoleType.RoleAdmin.name()) + .requestMatchers("/panel_user").hasAuthority(Role.RoleType.RoleAdmin.name()) .requestMatchers("/api/**").hasAuthority(Role.RoleType.RoleAdmin.name()) .requestMatchers("/main").hasAnyAuthority(Role.RoleType.RoleUser.name(), Role.RoleType.RoleAdmin.name()) .requestMatchers("/"+ FileServerController.URL_PATH +"/**").permitAll() diff --git a/src/main/java/com/alterdekim/game/service/AvatarInventoryService.java b/src/main/java/com/alterdekim/game/service/AvatarInventoryService.java index 5507e7e..00cf2b8 100644 --- a/src/main/java/com/alterdekim/game/service/AvatarInventoryService.java +++ b/src/main/java/com/alterdekim/game/service/AvatarInventoryService.java @@ -172,7 +172,7 @@ public class AvatarInventoryService { } public List getMagicAbilitiesItemsByUserId(Integer userId) { - return this.inventoryRepository.findMagicAbilitiesByUserId(userId); // todo: implement expiration ability + return this.inventoryRepository.findMagicAbilitiesByUserId(userId); } public List getSmilesByUserId(Integer userId) { diff --git a/src/main/java/com/alterdekim/game/service/UserService.java b/src/main/java/com/alterdekim/game/service/UserService.java index ea329ce..fe5a8e1 100644 --- a/src/main/java/com/alterdekim/game/service/UserService.java +++ b/src/main/java/com/alterdekim/game/service/UserService.java @@ -58,6 +58,10 @@ public class UserService { return userRepository.findByUsername(username); } + public List getAll() { + return this.userRepository.findAll(); + } + public int saveUser(String username, String password, Role.RoleType role) { User user = new User(); user.setUsername(username); @@ -87,6 +91,10 @@ public class UserService { }); } + public void removeUserProperty(Integer userId, PlayerProperties type) { + this.propertyRepository.deleteByUserIdAndType(userId, type); + } + public void pushUserProperty(Integer userId, PlayerProperties type, Object val) { this.propertyRepository.deleteByUserIdAndType(userId, type); this.propertyRepository.save(switch (type.getValueType()) { diff --git a/src/main/resources/_js/user.js b/src/main/resources/_js/user.js new file mode 100644 index 0000000..0a7c7ba --- /dev/null +++ b/src/main/resources/_js/user.js @@ -0,0 +1,118 @@ +const roleFlags = [ + "NONE", + "MEMBER", + "SA", + "MODERATOR", + "ADMINISTRATOR", + "CHIEF", + "HIDDEN" +]; + +const clubAccessType = [ + "OPEN", + "CLOSED", + "FRIENDS_ONLY", +]; + +function initPropertyTable(tag, data) { + if( data.length <= 0 ) return; + let keys = Object.keys(data[0]); + let header = ""; + for( let i = 1; i < keys.length; i++ ) { + header += "" + keys[i] + ""; + } + header += "actions"; + $($(tag).children("thead")[0]).html(""+header+""); + for( let i = 0; i < data.length; i++ ) { + header = ""; + let entry_type = ""; + let entry_name = ""; + for( let u = 0; u < keys.length; u++ ) { + if( u == 0 ) { + entry_type = data[i][keys[u]]; + header += ""+data[i][keys[u]]+""; + continue; + } else if( u == 1 ) { + entry_name = data[i][keys[u]]; + } else if( u == 2 ) { + switch(entry_type) { + case "Enum": + header += ""; + break; + case "Boolean": + let v = ""; + if( data[i][keys[u]]+"" == "true" ) { + v = "checked"; + } + header += ""; + break; + default: // Integer + header += ""; + } + continue; + } + header += "" + data[i][keys[u]] + ""; + } + header += ""; + header += ""; + let tabName = tag.substring(1); + $($(tag).children("tbody")[0]).append(""+header+""); + } +} + +function editRow(obj) { + let entry_name = $(obj).parent().parent().find("td[data-name='name']").html(); + let entry_value = $(obj).parent().parent().find("td[data-name='value']"); + let entry_type = $(obj).parent().parent().find(".entry-type").html(); + if( entry_type == "Enum") { + entry_value = entry_value.find("select").find(":selected").val(); + } else if( entry_type == "Boolean" ) { + entry_value = entry_value.find("input").is(":checked"); + } else { + entry_value = entry_value.find("input").val(); + } + console.log(entry_name, entry_type, entry_value); + + $.post( "/api/edit_user_property?user_id="+$("#user_init_info").attr("data-id")+"&entry_name="+entry_name+"&entry_value="+entry_value, function() { + window.location.reload(); + }); +} + +function resetRow(obj) { + let entry_name = $(obj).parent().parent().find("td[data-name='name']").html(); + $.post( "/api/reset_user_property?user_id="+$("#user_init_info").attr("data-id")+"&entry_name="+entry_name, function() { + window.location.reload(); + }); +} + +function set_loading(obj, state) { + $(obj).attr("aria-busy", state); +} + +function getUserProperties() { + $.get( "/api/get_user_properties?id="+$("#user_init_info").attr("data-id"), function( data ) { + initPropertyTable("#properties", data); + set_loading("#properties", false); + }); +} + +getUserProperties(); \ No newline at end of file diff --git a/src/main/resources/_js/users.js b/src/main/resources/_js/users.js new file mode 100644 index 0000000..e2106a8 --- /dev/null +++ b/src/main/resources/_js/users.js @@ -0,0 +1,18 @@ +function updateUsersTable() { + let rows = $("tbody").find("tr"); + for( let i = 0; i < rows.length; i++ ) { + let row = rows[i]; + let userId = $(row).attr("data-rownum"); + $($(row).find("td")[1]).html(""+$($(row).find("td")[1]).html()+""); + } +} + +function getUsers() { + $.get( "/api/get_entity_rows?entity=Users", function( data ) { + initTable("#users", data); + updateUsersTable(); + set_loading("#users", false); + }); +} + +getUsers(); \ No newline at end of file diff --git a/src/main/resources/css/panel.css b/src/main/resources/css/panel.css new file mode 100644 index 0000000..b212f89 --- /dev/null +++ b/src/main/resources/css/panel.css @@ -0,0 +1,20 @@ +aside > nav > ul > li { + font-size: 18px; +} + +.del-btn { + padding: 0.3rem 0.5rem; +} + +.add-btn { + margin-left: 0.2rem; + cursor: pointer; + width: 24px; + height: 24px; +} + +td > img { + width: 28px; + height: 28px; + cursor: pointer; +} \ No newline at end of file diff --git a/src/main/resources/templates/fragments/aside.html b/src/main/resources/templates/fragments/aside.html new file mode 100644 index 0000000..6f0c214 --- /dev/null +++ b/src/main/resources/templates/fragments/aside.html @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/head.html b/src/main/resources/templates/fragments/head.html new file mode 100644 index 0000000..4752379 --- /dev/null +++ b/src/main/resources/templates/fragments/head.html @@ -0,0 +1,9 @@ + + + + + + + WhimsyWorld + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html new file mode 100644 index 0000000..9c3abc4 --- /dev/null +++ b/src/main/resources/templates/fragments/header.html @@ -0,0 +1,15 @@ + +
+
+ +
+
+
\ No newline at end of file diff --git a/src/main/resources/templates/panel.html b/src/main/resources/templates/panel.html index 0eaadd9..9f0e8e4 100644 --- a/src/main/resources/templates/panel.html +++ b/src/main/resources/templates/panel.html @@ -1,68 +1,12 @@ - - - - - - WhimsyWorld - + -
-
- -
-
+
- +
@@ -119,6 +63,15 @@ + +

Users

+ + + + + +
+