Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.With;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

import java.time.Instant;
import java.util.List;
Expand All @@ -17,7 +18,9 @@ public record EventData(@NonNull String id,
@NonNull DiscordRoles discordRoles,
@NonNull String mod_loader,
@NonNull String minecraft_version,
@NonNull String modpack) implements Data {
@NonNull String modpack,
@Nullable String discordWebhookUrl
) implements Data {
public enum Type {
MODFEST,
BLANKETCON
Expand Down
16 changes: 12 additions & 4 deletions common/src/main/java/net/modfest/platform/pojo/SubmissionData.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public record SubmissionData(@NonNull String id,
@Nullable String modId,
@Nullable BoothData boothData,
@NonNull Awards awards
) implements Data {
) implements Data {

public record Awards(Set<String> theme, Set<String> extra) {
}
Expand Down Expand Up @@ -69,8 +69,8 @@ public JsonElement serialize(AssociatedData src,
var jsonObj = context.serialize(src.inner).getAsJsonObject();

var typeKey = switch (src.inner) {
case Modrinth a -> Modrinth.KEY;
case Other a -> Other.KEY;
case Modrinth ignored -> Modrinth.KEY;
case Other ignored -> Other.KEY;
default -> throw new IllegalStateException();
};
jsonObj.addProperty("type", typeKey);
Expand All @@ -94,7 +94,11 @@ public enum BoothStatus {
COMPLETE
}

public record Column(int x, int z) {}
public record Column(int x, int z) {
public String toFormattedString() {
return "x: " + x + ", z: " + z;
}
}

public record WarpCoordinates(int x, int y, int z, @NonNull Direction direction) {
@Getter
Expand Down Expand Up @@ -122,6 +126,10 @@ public enum Direction {
this.yaw = yaw;
}
}

public String toFormattedString() {
return "x: " + x + ", z: " + z + ", direction: " + direction.name();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
@EnableAsync
@ConfigurationPropertiesScan
public class PlatformApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import net.modfest.platform.service.EventService;
import net.modfest.platform.service.ImageService;
import net.modfest.platform.service.SubmissionService;
import net.modfest.platform.service.WebhookService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -33,6 +34,8 @@ public class EventController {
private UserController userController;
@Autowired
private ImageService imageService;
@Autowired
private WebhookService webhookService;

@GetMapping("/events")
public Collection<EventData> getAllEvents() {
Expand Down Expand Up @@ -297,9 +300,10 @@ public SubmissionResponseData editSubmissionImage(HttpServletRequest request, @P
};

imageService.downloadSubmissionImage(url, new SubmissionRepository.SubmissionId(eventId, subId), typeEnum);
webhookService.submissionImageChanged(event, submission, typeEnum);

return service.addResponseInfo(
request, service.getSubmission(eventId, subId)
request, submission
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void save(@NonNull Data newData) {
}

@Locked.Write("dataLock")
public void delete(@NonNull Id id) {
public Data delete(@NonNull Id id) {
var data = this.get(id);
this.root.write(p -> {
try {
Expand All @@ -136,6 +136,8 @@ public void delete(@NonNull Id id) {

this.store.remove(id);
onDataUpdated.emit(id);

return data;
}

@Locked.Read("dataLock")
Expand All @@ -155,6 +157,11 @@ public Collection<Data> getAll() {
return this.store.values();
}

@Locked.Read("dataLock")
public int size() {
return this.store.size();
}

private void validateId(@NonNull String id) throws ConstraintViolationException {
if (id.contains("/")) {
throw new ConstraintViolationException("Illegal character '/' in id: '"+id+"'");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.modfest.platform.service;

import jakarta.annotation.Nullable;
import jakarta.servlet.http.HttpServletRequest;
import net.modfest.platform.configuration.PlatformConfig;
import net.modfest.platform.repository.ImageRepository;
Expand All @@ -25,14 +26,18 @@ public void downloadSubmissionImage(String url, SubmissionRepository.SubmissionI
}
}

public String getImageUrl(HttpServletRequest request, SubmissionRepository.SubmissionId subKey, SubmissionImageType type) {
public String getImageUrl(@Nullable HttpServletRequest request, SubmissionRepository.SubmissionId subKey, SubmissionImageType type) {
var imageInfo = repository.getImageInfo(getImageLocationKey(subKey, type));
if (imageInfo == null) {
return null;
}

String imageCdn = config.getImageCdnUrl();
if (imageCdn == null) {
if(request == null) {
return null;
}

// Platform will be acting as the cdn
var baseUrl = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort();
imageCdn = baseUrl+"/imagecdn/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class SubmissionService {
private SubmissionRepository submissionRepository;
@Autowired
private UserService userService;
@Autowired
private WebhookService webhookService;
@Lazy
@Autowired
private EventService eventService;
Expand Down Expand Up @@ -63,6 +65,8 @@ public SubmissionData editSubmission(SubmissionData data, SubmissionPatchData ed
throw new IllegalStateException();
}
}

webhookService.editSubmission(eventService.getEventById(data.event()), edit, data);
submissionRepository.save(data);
return getSubmission(data.event(), data.id());
}
Expand All @@ -73,11 +77,15 @@ public SubmissionData editSubmissionBooth(SubmissionData data, SubmissionData.Bo
if (edit.warp() == null) edit = edit.withWarp(data.boothData().warp());
if (edit.itemIcon() == null) edit = edit.withItemIcon(data.boothData().itemIcon());
if (edit.shards() == null) edit = edit.withShards(data.boothData().shards());
if (edit.minutesToComplete() == null) edit = edit.withMinutesToComplete(data.boothData().minutesToComplete());
if (edit.minutesToComplete() == null)
edit = edit.withMinutesToComplete(data.boothData().minutesToComplete());
if (edit.status() == null) edit = edit.withStatus(data.boothData().status());
}
data = data.withBoothData(edit);
submissionRepository.save(data);

webhookService.editSubmissionBooth(eventService.getEventById(data.event()), data, edit);

return getSubmission(data.event(), data.id());
}

Expand All @@ -92,7 +100,10 @@ public SubmissionData updateSubmissionVersion(SubmissionData data) {
throw new IllegalArgumentException("Modrinth project not found!");
}

var latest = getLatestModrinth(mr.projectId(), eventService.getEventById(data.event()), project.projectType);
var event = eventService.getEventById(data.event());

var versions = getModrinthVersions(mr.projectId(), event, project.projectType);
var latest = pickModrinthLatest(versions);

var newData = data.withPlatform(new SubmissionData.AssociatedData(
new SubmissionData.AssociatedData.Modrinth(
Expand All @@ -101,6 +112,9 @@ public SubmissionData updateSubmissionVersion(SubmissionData data) {
)
));

if(latest != null && !Objects.equals(mr.versionId(), latest.id)) {
webhookService.updateSubmissionVersion(event, newData, latest, mr, versions);
}
submissionRepository.save(newData);
return getSubmission(newData.event(), newData.id());
}
Expand Down Expand Up @@ -172,11 +186,13 @@ public SubmissionData addSubmissionAuthor(SubmissionData data, UserData author)
}

public void deleteSubmission(String eventId, String subId) {
submissionRepository.delete(new SubmissionRepository.SubmissionId(eventId, subId));
var data = submissionRepository.delete(new SubmissionRepository.SubmissionId(eventId, subId));
webhookService.deleteSubmission(eventService.getEventById(eventId), data);
}

/**
* Retrieve all submissions made by a user
*
* @param filter If non-null, only submissions associated with that event will be returned
*/
public Stream<SubmissionData> getSubmissionsFromUser(UserData user, @Nullable EventData filter) {
Expand All @@ -191,8 +207,8 @@ public Stream<SubmissionData> getSubmissionsFromUser(UserData user, @Nullable Ev

public Stream<SubmissionData> getSubmissionsFromEvent(EventData event) {
return submissionRepository.getAll()
.stream()
.filter(s -> s.event().equals(event.id()));
.stream()
.filter(s -> s.event().equals(event.id()));
}

public Stream<UserData> getUsersForRinthProject(String modrinthProjectId) {
Expand All @@ -203,9 +219,9 @@ public Stream<UserData> getUsersForRinthProject(String modrinthProjectId) {
if (members == null) return null;

return Stream.concat(
members.stream(),
(organization == null ? List.<TeamMember>of() : organization.members).stream()
).map(mrData -> userService.getByModrinthId(mrData.user.id))
members.stream(),
(organization == null ? List.<TeamMember>of() : organization.members).stream()
).map(mrData -> userService.getByModrinthId(mrData.user.id))
.filter(Objects::nonNull);
}

Expand Down Expand Up @@ -243,6 +259,7 @@ public SubmissionData makeSubmissionOther(EventData event, Set<UserData> authors
)
);
submissionRepository.save(submission);
webhookService.makeSubmission(event, submission, submissionRepository.size());
return submission;
}

Expand All @@ -256,42 +273,47 @@ public SubmissionData makeSubmissionModrinth(String eventId, Set<UserData> autho
throw new RuntimeException("submission already exists");
}

var event = eventService.getEventById(eventId);

var versions = getModrinthVersions(subId, event, project.projectType);
var latest = pickModrinthLatest(versions);
if (project.sourceUrl == null || project.sourceUrl.isBlank()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Modrinth submissions MUST have visible source code!");
}

var latest = getLatestModrinth(subId, eventService.getEventById(eventId), project.projectType);

if (project.iconUrl != null) {
imageService.downloadSubmissionImage(project.iconUrl, subKey, ImageService.SubmissionImageType.ICON);
}
var galleryUrl = getGalleryUrl(project);
if (galleryUrl != null) {
imageService.downloadSubmissionImage(galleryUrl, subKey, ImageService.SubmissionImageType.SCREENSHOT);
}
submissionRepository.save(
new SubmissionData(
subId,
eventId,
project.title,
project.description,
authors.stream().map(UserData::id).collect(Collectors.toSet()),
new SubmissionData.AssociatedData(
new SubmissionData.AssociatedData.Modrinth(
project.id,
latest == null ? null : latest.id
)
),
project.sourceUrl,
null,
null,
new SubmissionData.Awards(
Set.of(),
Set.of()

var submission = new SubmissionData(
subId,
eventId,
project.title,
project.description,
authors.stream().map(UserData::id).collect(Collectors.toSet()),
new SubmissionData.AssociatedData(
new SubmissionData.AssociatedData.Modrinth(
project.id,
latest == null ? null : latest.id
)
),
project.sourceUrl,
null,
null,
new SubmissionData.Awards(
Set.of(),
Set.of()
)
);

submissionRepository.save(submission);
webhookService.makeSubmission(event, submission, submissionRepository.size());

return submissionRepository.get(subKey);
}

Expand All @@ -305,17 +327,25 @@ public SubmissionData makeSubmissionModrinth(String eventId, Set<UserData> autho
}

/**
* Retrieves the most recent version of a modrinth project
* Retrieves fitting versions of a modrinth project
*
* @param event The event this version will be for. Used for filtering
* @param projectType The modrinth project type. Used for filtering
*/
private @Nullable Version getLatestModrinth(String mrProject, EventData event, String projectType) {
private @Nullable List<Version> getModrinthVersions(String mrProject, EventData event, String projectType) {
if (projectType.equals("modpack")) return null;
var filter = VersionFilter.ofLoaders(List.of(event.mod_loader(), "minecraft", "datapack", "iris"))
.andGameVersion(event.minecraft_version());
return modrinth.projects().getVersions(mrProject, filter)
.stream()

return modrinth.projects().getVersions(mrProject, filter);
}

private @Nullable Version pickModrinthLatest(@Nullable List<Version> versions) {
if (versions == null) {
return null;
}

return versions.stream()
.max(Comparator.comparing(v -> v.datePublished))
.orElse(null);
}
Expand Down
Loading
Loading