Skip to content

Commit a353a60

Browse files
committed
Implement user validation service and bedrock checks
- Added UserValidationService to handle user validation logic. - Introduced interfaces for online player lookup, stored user lookup, and server history lookup. - Created BedrockCheckResult and UserValidationResult classes for structured validation results. - Implemented various validation strategies including online checks and bedrock prechecks. - Added unit tests for UserValidationService to ensure functionality.
1 parent d2a7ca1 commit a353a60

17 files changed

+609
-0
lines changed

AdvancedCore/src/main/java/com/bencodez/advancedcore/api/bedrock/BedrockNameResolver.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,51 @@ public Result resolve(String incomingName) {
333333
return new Result(incomingName, false, "unknown-default-java");
334334
}
335335

336+
public Result resolveWithoutDb(String incomingName) {
337+
if (incomingName == null || incomingName.isEmpty()) {
338+
return new Result(incomingName, false, "empty-name");
339+
}
340+
341+
// Online - match exact and stripped forms
342+
Player match = findOnlineByNameOrStripped(incomingName);
343+
if (match != null) {
344+
boolean bedrock = bedrockDetect.isBedrock(match.getUniqueId());
345+
346+
String finalName = incomingName;
347+
if (bedrock) {
348+
finalName = addPrefixIfNeeded(match.getName(), true);
349+
}
350+
351+
return new Result(finalName, bedrock, bedrock ? "online-uuid-bedrock" : "online-uuid-java");
352+
}
353+
354+
// Cache on incoming name
355+
Boolean cached = getCachedCaseInsensitive(incomingName);
356+
if (cached != null) {
357+
boolean bedrock = cached;
358+
String finalName = addPrefixIfNeeded(incomingName, bedrock);
359+
return new Result(finalName, bedrock, "cache-" + (bedrock ? "bedrock" : "java"));
360+
}
361+
362+
// Cache on prefixed variant
363+
String prefixed = buildPrefixedVariant(incomingName);
364+
if (prefixed != null) {
365+
Boolean cachedPrefixed = getCachedCaseInsensitive(prefixed);
366+
if (cachedPrefixed != null) {
367+
boolean bedrock = cachedPrefixed;
368+
String finalName = bedrock ? prefixed : incomingName;
369+
return new Result(finalName, bedrock, "cache-" + (bedrock ? "bedrock" : "java") + "-prefixed-variant");
370+
}
371+
}
372+
373+
// Prefix-only fallback
374+
if (bedrockPrefix != null && !bedrockPrefix.isEmpty() && incomingName.startsWith(bedrockPrefix)) {
375+
return new Result(incomingName, true, "prefixed-only");
376+
}
377+
378+
return new Result(incomingName, false, "unknown-no-db");
379+
}
380+
336381
/**
337382
* Get the prefixed name if the player is a Bedrock player.
338383
*

AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/UserManager.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import com.bencodez.advancedcore.AdvancedCorePlugin;
1616
import com.bencodez.advancedcore.api.player.UuidLookup;
1717
import com.bencodez.advancedcore.api.user.usercache.UserDataManager;
18+
import com.bencodez.advancedcore.api.user.validation.UserValidationFactory;
19+
import com.bencodez.advancedcore.api.user.validation.UserValidationService;
1820
import com.bencodez.simpleapi.array.ArrayUtils;
1921
import com.bencodez.simpleapi.sql.Column;
2022
import com.bencodez.simpleapi.sql.DataType;
@@ -39,6 +41,9 @@ public class UserManager {
3941
@Getter
4042
private final ArrayList<UserDataChanged> userDataChange = new ArrayList<>();
4143

44+
@Getter
45+
private UserValidationService validationService;
46+
4247
public UserManager(AdvancedCorePlugin plugin) {
4348
this.plugin = plugin;
4449
load();
@@ -381,6 +386,7 @@ public AdvancedCoreUser getUser(UUID uuid, String playerName) {
381386

382387
public void load() {
383388
dataManager = new UserDataManager(AdvancedCorePlugin.getInstance());
389+
validationService = UserValidationFactory.create(plugin);
384390
}
385391

386392
public void onChange(AdvancedCoreUser user, String... keys) {
@@ -474,6 +480,25 @@ public void removeAllKeyValues(String key, DataType type) {
474480
}
475481
}
476482

483+
public boolean userExistStored(String name) {
484+
if (name == null || name.isEmpty()) {
485+
return false;
486+
}
487+
488+
boolean exist = ArrayUtils.containsIgnoreCase(getAllPlayerNames(), name);
489+
if (exist) {
490+
return true;
491+
}
492+
493+
try {
494+
UUID u = UUID.fromString(name);
495+
return userExist(u);
496+
} catch (Exception ignored) {
497+
}
498+
499+
return false;
500+
}
501+
477502
public void removeUUID(UUID key) {
478503
if (plugin.getStorageType().equals(UserStorage.SQLITE)) {
479504
plugin.getSQLiteUserTable().delete(new Column("uuid", new DataValueString(key.toString())));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.bencodez.advancedcore.api.user.validation;
2+
3+
public class BedrockCheckResult {
4+
5+
private final boolean bedrock;
6+
private final boolean trusted;
7+
private final String normalizedName;
8+
private final String reason;
9+
10+
public BedrockCheckResult(boolean bedrock, boolean trusted, String normalizedName, String reason) {
11+
this.bedrock = bedrock;
12+
this.trusted = trusted;
13+
this.normalizedName = normalizedName;
14+
this.reason = reason;
15+
}
16+
17+
public boolean isBedrock() {
18+
return bedrock;
19+
}
20+
21+
public boolean isTrusted() {
22+
return trusted;
23+
}
24+
25+
public String getNormalizedName() {
26+
return normalizedName;
27+
}
28+
29+
public String getReason() {
30+
return reason;
31+
}
32+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.bencodez.advancedcore.api.user.validation;
2+
3+
import com.bencodez.advancedcore.AdvancedCorePlugin;
4+
import com.bencodez.advancedcore.api.user.validation.impl.AdvancedCoreBedrockPrecheck;
5+
import com.bencodez.advancedcore.api.user.validation.impl.AdvancedCoreStoredUserLookup;
6+
import com.bencodez.advancedcore.api.user.validation.impl.BukkitOnlinePlayerLookup;
7+
import com.bencodez.advancedcore.api.user.validation.impl.BukkitServerHistoryLookup;
8+
9+
public class UserValidationFactory {
10+
11+
private UserValidationFactory() {
12+
}
13+
14+
public static UserValidationService create(AdvancedCorePlugin plugin) {
15+
return new UserValidationService(
16+
new BukkitOnlinePlayerLookup(),
17+
new AdvancedCoreStoredUserLookup(plugin),
18+
new BukkitServerHistoryLookup(),
19+
new AdvancedCoreBedrockPrecheck(plugin));
20+
}
21+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.bencodez.advancedcore.api.user.validation;
2+
3+
public class UserValidationResult {
4+
5+
private final ValidationStatus status;
6+
private final String normalizedName;
7+
private final ValidationSource source;
8+
private final String reason;
9+
private final boolean bedrock;
10+
11+
public UserValidationResult(ValidationStatus status,
12+
String normalizedName,
13+
ValidationSource source,
14+
String reason,
15+
boolean bedrock) {
16+
this.status = status;
17+
this.normalizedName = normalizedName;
18+
this.source = source;
19+
this.reason = reason;
20+
this.bedrock = bedrock;
21+
}
22+
23+
public boolean isValid() {
24+
return status == ValidationStatus.VALID;
25+
}
26+
27+
public ValidationStatus getStatus() {
28+
return status;
29+
}
30+
31+
public String getNormalizedName() {
32+
return normalizedName;
33+
}
34+
35+
public ValidationSource getSource() {
36+
return source;
37+
}
38+
39+
public String getReason() {
40+
return reason;
41+
}
42+
43+
public boolean isBedrock() {
44+
return bedrock;
45+
}
46+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.bencodez.advancedcore.api.user.validation;
2+
3+
import com.bencodez.advancedcore.api.user.validation.interfaces.*;
4+
5+
public class UserValidationService {
6+
7+
private final OnlinePlayerLookup online;
8+
private final StoredUserLookup storage;
9+
private final ServerHistoryLookup serverHistory;
10+
private final BedrockPrecheck bedrock;
11+
12+
public UserValidationService(OnlinePlayerLookup online, StoredUserLookup storage, ServerHistoryLookup serverHistory,
13+
BedrockPrecheck bedrock) {
14+
this.online = online;
15+
this.storage = storage;
16+
this.serverHistory = serverHistory;
17+
this.bedrock = bedrock;
18+
}
19+
20+
public UserValidationResult validate(String inputName, boolean checkServerHistory) {
21+
if (inputName == null) {
22+
return new UserValidationResult(ValidationStatus.INVALID, "", ValidationSource.UNKNOWN, "null-name", false);
23+
}
24+
25+
String name = inputName.trim();
26+
if (name.isEmpty()) {
27+
return new UserValidationResult(ValidationStatus.INVALID, "", ValidationSource.UNKNOWN, "empty-name",
28+
false);
29+
}
30+
31+
if (online.isOnlineExact(name)) {
32+
return new UserValidationResult(ValidationStatus.VALID, name, ValidationSource.ONLINE_PLAYER,
33+
"online-player", false);
34+
}
35+
36+
if (storage.userExistsStored(name)) {
37+
return new UserValidationResult(ValidationStatus.VALID, name, ValidationSource.STORAGE, "stored-user",
38+
false);
39+
}
40+
41+
var bedrockResult = bedrock.check(name);
42+
43+
if (bedrockResult.isBedrock() && bedrockResult.isTrusted()) {
44+
return new UserValidationResult(ValidationStatus.VALID, bedrockResult.getNormalizedName(),
45+
ValidationSource.BEDROCK_TRUSTED, bedrockResult.getReason(), true);
46+
}
47+
48+
if (checkServerHistory && serverHistory.hasJoinedBefore(name)) {
49+
return new UserValidationResult(ValidationStatus.VALID, name, ValidationSource.SERVER_HISTORY,
50+
"server-history", false);
51+
}
52+
53+
if (bedrockResult.isBedrock()) {
54+
return new UserValidationResult(ValidationStatus.INVALID, bedrockResult.getNormalizedName(),
55+
ValidationSource.BEDROCK_UNTRUSTED, "untrusted-bedrock", true);
56+
}
57+
58+
return new UserValidationResult(ValidationStatus.INVALID, name, ValidationSource.UNKNOWN, "unknown-user",
59+
false);
60+
}
61+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.bencodez.advancedcore.api.user.validation;
2+
3+
public enum ValidationSource {
4+
ONLINE_PLAYER,
5+
STORAGE,
6+
SERVER_HISTORY,
7+
BEDROCK_TRUSTED,
8+
BEDROCK_UNTRUSTED,
9+
UNKNOWN
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.bencodez.advancedcore.api.user.validation;
2+
3+
public enum ValidationStatus {
4+
VALID,
5+
INVALID
6+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.bencodez.advancedcore.api.user.validation.impl;
2+
3+
import com.bencodez.advancedcore.AdvancedCorePlugin;
4+
import com.bencodez.advancedcore.api.bedrock.BedrockNameResolver;
5+
import com.bencodez.advancedcore.api.user.validation.BedrockCheckResult;
6+
import com.bencodez.advancedcore.api.user.validation.interfaces.BedrockPrecheck;
7+
8+
public class AdvancedCoreBedrockPrecheck implements BedrockPrecheck {
9+
10+
private final AdvancedCorePlugin plugin;
11+
12+
public AdvancedCoreBedrockPrecheck(AdvancedCorePlugin plugin) {
13+
this.plugin = plugin;
14+
}
15+
16+
@Override
17+
public BedrockCheckResult check(String name) {
18+
if (name == null || name.isEmpty()) {
19+
return new BedrockCheckResult(false, false, "", "empty-name");
20+
}
21+
22+
BedrockNameResolver.Result result = plugin.getBedrockHandle().resolveWithoutDb(name);
23+
24+
boolean trusted = !"prefixed-only".equals(result.rationale)
25+
&& !"unknown-no-db".equals(result.rationale);
26+
27+
return new BedrockCheckResult(result.isBedrock, trusted, result.finalName, result.rationale);
28+
}
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.bencodez.advancedcore.api.user.validation.impl;
2+
3+
import com.bencodez.advancedcore.AdvancedCorePlugin;
4+
import com.bencodez.advancedcore.api.user.validation.interfaces.StoredUserLookup;
5+
6+
public class AdvancedCoreStoredUserLookup implements StoredUserLookup {
7+
8+
private final AdvancedCorePlugin plugin;
9+
10+
public AdvancedCoreStoredUserLookup(AdvancedCorePlugin plugin) {
11+
this.plugin = plugin;
12+
}
13+
14+
@Override
15+
public boolean userExistsStored(String name) {
16+
if (name == null || name.isEmpty()) {
17+
return false;
18+
}
19+
return plugin.getUserManager().userExistStored(name);
20+
}
21+
}

0 commit comments

Comments
 (0)