diff --git a/gemp-lotr/gemp-lotr-async/pom.xml b/gemp-lotr/gemp-lotr-async/pom.xml index 3f2b06757..bf4de2844 100644 --- a/gemp-lotr/gemp-lotr-async/pom.xml +++ b/gemp-lotr/gemp-lotr-async/pom.xml @@ -27,6 +27,11 @@ log4j 1.2.16 + + com.google.code.gson + gson + 2.8.2 + diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/GempukkuHttpRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/GempukkuHttpRequestHandler.java index 4a717211e..75c9d4f78 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/GempukkuHttpRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/GempukkuHttpRequestHandler.java @@ -279,6 +279,18 @@ public class GempukkuHttpRequestHandler extends SimpleChannelInboundHandler headers) { HttpHeaders headers1 = convertToHeaders(headers); diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/ResponseWriter.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/ResponseWriter.java index ba92f0c66..ad0f1c366 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/ResponseWriter.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/ResponseWriter.java @@ -12,6 +12,7 @@ public interface ResponseWriter { public void writeFile(File file, Map headers); public void writeHtmlResponse(String html); + public void writeJsonResponse(String html); public void writeByteResponse(byte[] bytes, Map headers); diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/GameHistoryRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/GameHistoryRequestHandler.java index f70546d0e..d25b0998b 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/GameHistoryRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/GameHistoryRequestHandler.java @@ -54,27 +54,27 @@ public class GameHistoryRequestHandler extends LotroServerRequestHandler impleme for (GameHistoryEntry gameHistoryEntry : playerGameHistory) { Element historyEntry = doc.createElement("historyEntry"); - historyEntry.setAttribute("winner", gameHistoryEntry.getWinner()); - historyEntry.setAttribute("loser", gameHistoryEntry.getLoser()); + historyEntry.setAttribute("winner", gameHistoryEntry.winner); + historyEntry.setAttribute("loser", gameHistoryEntry.loser); - historyEntry.setAttribute("winReason", gameHistoryEntry.getWinReason()); - historyEntry.setAttribute("loseReason", gameHistoryEntry.getLoseReason()); + historyEntry.setAttribute("winReason", gameHistoryEntry.win_reason); + historyEntry.setAttribute("loseReason", gameHistoryEntry.lose_reason); - historyEntry.setAttribute("formatName", gameHistoryEntry.getFormatName()); - String tournament = gameHistoryEntry.getTournament(); + historyEntry.setAttribute("formatName", gameHistoryEntry.format_name); + String tournament = gameHistoryEntry.tournament; if (tournament != null) historyEntry.setAttribute("tournament", tournament); - if (gameHistoryEntry.getWinner().equals(resourceOwner.getName()) && gameHistoryEntry.getWinnerRecording() != null) { - historyEntry.setAttribute("gameRecordingId", gameHistoryEntry.getWinnerRecording()); - historyEntry.setAttribute("deckName", gameHistoryEntry.getWinnerDeckName()); - } else if (gameHistoryEntry.getLoser().equals(resourceOwner.getName()) && gameHistoryEntry.getLoserRecording() != null) { - historyEntry.setAttribute("gameRecordingId", gameHistoryEntry.getLoserRecording()); - historyEntry.setAttribute("deckName", gameHistoryEntry.getLoserDeckName()); + if (gameHistoryEntry.winner.equals(resourceOwner.getName()) && gameHistoryEntry.win_recording_id != null) { + historyEntry.setAttribute("gameRecordingId", gameHistoryEntry.win_recording_id); + historyEntry.setAttribute("deckName", gameHistoryEntry.winner_deck_name); + } else if (gameHistoryEntry.loser.equals(resourceOwner.getName()) && gameHistoryEntry.lose_recording_id != null) { + historyEntry.setAttribute("gameRecordingId", gameHistoryEntry.lose_recording_id); + historyEntry.setAttribute("deckName", gameHistoryEntry.loser_deck_name); } - historyEntry.setAttribute("startTime", String.valueOf(gameHistoryEntry.getStartTime().getTime())); - historyEntry.setAttribute("endTime", String.valueOf(gameHistoryEntry.getEndTime().getTime())); + historyEntry.setAttribute("startTime", String.valueOf(gameHistoryEntry.GetStartDate().getTime())); + historyEntry.setAttribute("endTime", String.valueOf(gameHistoryEntry.GetEndDate().getTime())); gameHistory.appendChild(historyEntry); } @@ -94,22 +94,22 @@ public class GameHistoryRequestHandler extends LotroServerRequestHandler impleme for (GameHistoryEntry gameHistoryEntry : playerGameHistory) { Element historyEntry = doc.createElement("historyEntry"); - historyEntry.setAttribute("winner", gameHistoryEntry.getWinner()); - historyEntry.setAttribute("loser", gameHistoryEntry.getLoser()); + historyEntry.setAttribute("winner", gameHistoryEntry.winner); + historyEntry.setAttribute("loser", gameHistoryEntry.loser); - historyEntry.setAttribute("winReason", gameHistoryEntry.getWinReason()); - historyEntry.setAttribute("loseReason", gameHistoryEntry.getLoseReason()); + historyEntry.setAttribute("winReason", gameHistoryEntry.win_reason); + historyEntry.setAttribute("loseReason", gameHistoryEntry.lose_reason); - historyEntry.setAttribute("formatName", gameHistoryEntry.getFormatName()); + historyEntry.setAttribute("formatName", gameHistoryEntry.format_name); - historyEntry.setAttribute("winnerRecordingLink", "http://www.gempukku.com/gemp-lotr/game.html?replayId="+gameHistoryEntry.getWinner()+"$"+gameHistoryEntry.getWinnerRecording()); - historyEntry.setAttribute("winnerDeckName", gameHistoryEntry.getWinnerDeckName()); + historyEntry.setAttribute("winnerRecordingLink", "http://www.gempukku.com/gemp-lotr/game.html?replayId="+gameHistoryEntry.winner+"$"+gameHistoryEntry.win_recording_id); + historyEntry.setAttribute("winnerDeckName", gameHistoryEntry.winner_deck_name); - historyEntry.setAttribute("loserRecordingLink", "http://www.gempukku.com/gemp-lotr/game.html?replayId="+gameHistoryEntry.getLoser()+"$"+gameHistoryEntry.getLoserRecording()); - historyEntry.setAttribute("loserDeckName", gameHistoryEntry.getLoserDeckName()); + historyEntry.setAttribute("loserRecordingLink", "http://www.gempukku.com/gemp-lotr/game.html?replayId="+gameHistoryEntry.loser+"$"+gameHistoryEntry.lose_recording_id); + historyEntry.setAttribute("loserDeckName", gameHistoryEntry.loser_deck_name); - historyEntry.setAttribute("startTime", dateFormat.format(new Date(gameHistoryEntry.getStartTime().getTime()))); - historyEntry.setAttribute("endTime", dateFormat.format(new Date(gameHistoryEntry.getEndTime().getTime()))); + historyEntry.setAttribute("startTime", dateFormat.format(new Date(gameHistoryEntry.GetStartDate().getTime()))); + historyEntry.setAttribute("endTime", dateFormat.format(new Date(gameHistoryEntry.GetEndDate().getTime()))); gameHistory.appendChild(historyEntry); } @@ -134,15 +134,15 @@ public class GameHistoryRequestHandler extends LotroServerRequestHandler impleme DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (GameHistoryEntry gameHistoryEntry : playerGameHistory) { - String winnerLink = "http://www.gempukku.com/gemp-lotr/game.html?replayId=" + gameHistoryEntry.getWinner() + "$" + gameHistoryEntry.getWinnerRecording(); - String loserLink = "http://www.gempukku.com/gemp-lotr/game.html?replayId=" + gameHistoryEntry.getLoser() + "$" + gameHistoryEntry.getLoserRecording(); + String winnerLink = "http://www.gempukku.com/gemp-lotr/game.html?replayId=" + gameHistoryEntry.winner + "$" + gameHistoryEntry.win_recording_id; + String loserLink = "http://www.gempukku.com/gemp-lotr/game.html?replayId=" + gameHistoryEntry.loser + "$" + gameHistoryEntry.lose_recording_id; sb.append(""); - sb.append("" + dateFormat.format(new Date(gameHistoryEntry.getStartTime().getTime())) + "" + dateFormat.format(new Date(gameHistoryEntry.getEndTime().getTime())) + ""); - sb.append("" + gameHistoryEntry.getWinner() + "" + gameHistoryEntry.getWinReason() + "" + gameHistoryEntry.getWinnerDeckName() + "Replay"); + sb.append("" + dateFormat.format(new Date(gameHistoryEntry.GetStartDate().getTime())) + "" + dateFormat.format(new Date(gameHistoryEntry.GetEndDate().getTime())) + ""); + sb.append("" + gameHistoryEntry.winner + "" + gameHistoryEntry.win_reason + "" + gameHistoryEntry.winner_deck_name + "Replay"); sb.append(""); sb.append(""); - sb.append("" + gameHistoryEntry.getLoser() + "" + gameHistoryEntry.getLoseReason() + "" + gameHistoryEntry.getLoserDeckName() + "Replay"); + sb.append("" + gameHistoryEntry.loser + "" + gameHistoryEntry.lose_reason + "" + gameHistoryEntry.lose_reason + "Replay"); sb.append(""); } diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/PlaytestRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/PlaytestRequestHandler.java index 9b8dac246..19b9e4150 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/PlaytestRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/PlaytestRequestHandler.java @@ -9,11 +9,9 @@ import com.gempukku.lotro.common.ApplicationConfiguration; import com.gempukku.lotro.db.LeagueDAO; import com.gempukku.lotro.db.PlayerDAO; import com.gempukku.lotro.db.vo.CollectionType; +import com.gempukku.lotro.db.vo.GameHistoryEntry; import com.gempukku.lotro.draft2.SoloDraftDefinitions; -import com.gempukku.lotro.game.CardCollection; -import com.gempukku.lotro.game.CardSets; -import com.gempukku.lotro.game.LotroCardBlueprintLibrary; -import com.gempukku.lotro.game.Player; +import com.gempukku.lotro.game.*; import com.gempukku.lotro.game.formats.LotroFormatLibrary; import com.gempukku.lotro.hall.HallServer; import com.gempukku.lotro.league.*; @@ -25,6 +23,7 @@ import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; import org.w3c.dom.Document; import org.w3c.dom.Element; +import com.google.gson.Gson; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -35,11 +34,13 @@ import java.util.*; public class PlaytestRequestHandler extends LotroServerRequestHandler implements UriRequestHandler { private PlayerDAO _playerDAO; + private GameHistoryService _gameHistoryService; + private Gson JsonConvert = new Gson(); public PlaytestRequestHandler(Map context) { super(context); _playerDAO = extractObject(context, PlayerDAO.class); - + _gameHistoryService = extractObject(context, GameHistoryService.class); } @Override @@ -50,6 +51,8 @@ public class PlaytestRequestHandler extends LotroServerRequestHandler implements removeTesterFlag(request, responseWriter); } else if (uri.equals("/getTesterFlag") && request.getMethod() == HttpMethod.GET) { getTesterFlag(request, responseWriter); + } else if (uri.equals("/getRecentReplays") && request.getMethod() == HttpMethod.POST) { + getRecentReplays(request, responseWriter); } else { throw new HttpProcessingException(404); } @@ -102,4 +105,20 @@ public class PlaytestRequestHandler extends LotroServerRequestHandler implements } } + private void getRecentReplays(HttpRequest request, ResponseWriter responseWriter) throws Exception { + HttpPostRequestDecoder postDecoder = new HttpPostRequestDecoder(request); + try { + + String format = getFormParameterSafely(postDecoder, "format"); + int count = Integer.parseInt(getFormParameterSafely(postDecoder, "count")); + + final List gameHistory = _gameHistoryService.getGameHistoryForFormat(format, count); + + responseWriter.writeJsonResponse(JsonConvert.toJson(gameHistory)); + + } finally { + postDecoder.destroy(); + } + } + } diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/hall.html b/gemp-lotr/gemp-lotr-async/src/main/web/hall.html index 153f9d0bd..99f35c092 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/hall.html +++ b/gemp-lotr/gemp-lotr-async/src/main/web/hall.html @@ -42,6 +42,8 @@ + + diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/includes/playtesting.html b/gemp-lotr/gemp-lotr-async/src/main/web/includes/playtesting.html index 5e0f8e7f4..73223774e 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/includes/playtesting.html +++ b/gemp-lotr/gemp-lotr-async/src/main/web/includes/playtesting.html @@ -12,5 +12,58 @@

If you have any questions or would like to discuss our card design decisions in-depth, then feel free to join us on the PC Discord server.

+
+ + Last 100 Playtest Replays: + +
+ + + + + + + + + + + + + + + +
FormatWinnerLoserWin ReasonLoss ReasonWinner ReplayLoser ReplayStartedEnded
+
+ + + diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/communication.js b/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/communication.js index 31281bf5b..955126eb9 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/communication.js +++ b/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/communication.js @@ -642,6 +642,21 @@ var GempLotrCommunication = Class.extend({ }); }, + getRecentReplays:function (format, count, callback, errorMap) { + $.ajax({ + type:"POST", + url:this.url + "/playtesting/getRecentReplays", + cache:false, + data:{ + format:format, + count:count + }, + success:this.deliveryCheck(callback), + error:this.errorCheck(errorMap), + dataType:"json" + }); + }, + getStatus:function (callback, errorMap) { $.ajax({ type:"GET", diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/LICENSE b/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/LICENSE new file mode 100644 index 000000000..360fb2952 --- /dev/null +++ b/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/LICENSE @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2021 JSON2HTML. https://json2html.com/ +======= + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/README.md b/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/README.md new file mode 100644 index 000000000..5fc11554a --- /dev/null +++ b/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/README.md @@ -0,0 +1,79 @@ + +json2html +------------------ + +json2html is an open source javascript library that uses js templates to render JSON objects into HTML. + +Build lightning fast, interactive client side templates using nothing but javascript. + +Free to use under the MIT license. + +www.json2html.com for full documentation. + +Features +-------------- + ++ Native JS templates that can be read within the browser or node.js and don't require compilation ++ One template for both the client and server ++ Interactive with embedded events directly in your templates ++ 100% Javascript so no need to learn any new syntax, use inline js functions for complex logic + +Example +-------------- +```javascript +json2html.render( + [ + {"name": "Justice League", "year":2021}, + {"name": "Coming 2 America", "year":2021} + ], + {"<>": "li", "html":[ + {"<>": "span", "text": "${name} (${year})"} + ]}); + +``` + +Will render the following html + +```html +
  • + Justice League (2021) +
  • +
  • + Coming 2 America (2021) +
  • ":"button","text":"Click Me","onclick":funciton(e){ + alert("You just clicked this"); + }}; +``` +Will render into the following html and will alert when clicked :) + +```html + +``` + +Node.js +========= +Use your temlpates seemlessly on Node.js (sorry no events here) + +Installation +------------ + + npm install node-json2html + + +Usage +----- +```javascript + const json2html = require('node-json2html'); + + let html = json2html.transform([{'male':'Bob','female':'Jane'},{'male':'Rick','female':'Ann'}],{"<>":"div","html":"${male} likes ${female}"}); +``` + +www.json2html.com for full documentation. diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/json2html.js b/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/json2html.js new file mode 100644 index 000000000..93189f4d0 --- /dev/null +++ b/gemp-lotr/gemp-lotr-async/src/main/web/js/lib/json2html/json2html.js @@ -0,0 +1,1066 @@ + +// json2html.js 2.0.0 +// https://www.json2html.com +// (c) 2006-2021 Crystalline Technologies +// json2html may be freely distributed under the MIT license. + +(function() { + + "use strict"; + + // Baseline setup + // -------------- + + // Establish the root object, `window` (`self`) in the browser, `global` + // on the server, or `this` in some virtual machines. We use `self` + // instead of `window` for `WebWorker` support. + var root = typeof self == 'object' && self.self === self && self || + typeof global == 'object' && global.global === global && global || + this || + {}; + + //Rendering types + // function(obj, template, index, options) + // returns iHTML object + var TYPES = { + + //HTML Element + "<>":function(obj, template, index, options){ + return(_html("<>",obj, template, index, options)); + }, + + //HTML Element (LEGACY Support) + "tag":function(obj, template, index, options){ + return(_html("tag",obj, template, index, options)); + }, + + //Component + "[]":_component + }; + + //Components {name:template} + var COMPONENTS = {}; + + /* ---------------------------------------- Interactive HTML Object (iHTML) ------------------------------------------------ */ + + function iHTML(html){ + + //Object type + this.type = "iHTML"; + + //html + this.html = html || ""; + + //associated events + this.events = []; + } + + //Append an ihtml object + // obj = ihtml OR html string + iHTML.prototype.append = function(obj){ + + if(obj) + if(obj.type === "iHTML") { + + //Append the html + this.html += obj.html; + + //Append the events + this.events = this.events.concat(obj.events); + } + + //Added for chaining + return(this); + }; + + //Append HTML to this object + iHTML.prototype.appendHTML = function(html){ + this.html += html; + }; + + //Spit out the object as json + iHTML.prototype.toJSON = function(){ + return({ + "html":this.html, + "events":this.events + }); + }; + + /* ---------------------------------------- Tokenizer ------------------------------------------------ */ + + function Tokenizer( tokenizers, doBuild ){ + + if( !(this instanceof Tokenizer ) ) + return new Tokenizer( tokenizers, onEnd, onFound ); + + this.tokenizers = tokenizers.splice ? tokenizers : [tokenizers]; + if( doBuild ) + this.doBuild = doBuild; + } + + Tokenizer.prototype.parse = function( src ){ + this.src = src; + this.ended = false; + this.tokens = [ ]; + do this.next(); while( !this.ended ); + return this.tokens; + }; + + Tokenizer.prototype.build = function( src, real ){ + if( src ) + this.tokens.push( + !this.doBuild ? src : + this.doBuild(src,real,this.tkn) + ); + }; + + Tokenizer.prototype.next = function(){ + var self = this, + plain; + + self.findMin(); + plain = self.src.slice(0, self.min); + + self.build( plain, false ); + + self.src = self.src.slice(self.min).replace(self.tkn,function( all ){ + self.build(all, true); + return ''; + }); + + if( !self.src ) + self.ended = true; + }; + + Tokenizer.prototype.findMin = function(){ + var self = this, i=0, tkn, idx; + self.min = -1; + self.tkn = ''; + + while(( tkn = self.tokenizers[i++]) !== undefined ){ + idx = self.src[tkn.test?'search':'indexOf'](tkn); + if( idx != -1 && (self.min == -1 || idx < self.min )){ + self.tkn = tkn; + self.min = idx; + } + } + if( self.min == -1 ) + self.min = self.src.length; + }; + + /* ---------------------------------------- Public Methods ------------------------------------------------ */ + root.json2html = { + + //Current version + "version":"2.0.0", + + //Render a json2html template + // obj : json object to render, or json string + // template: json2html template (array or json object) + // options : {} + // components : {name:template,...} + // output : ihtml | html + "render": function(obj,template,options) { + + //create the default object + var out = new iHTML(); + + //Default options + var _options = { + "output":"html" + }; + + //Parse the user defined options + if(options) { + + // LEGACY support for events, now output + if(options.events) _options.output = "ihtml"; + + //Add the other allowed options + _options.components = options.components; + _options.data = options.data; + + //Make sure we don't overwrite the default value + if(options.output) _options.output = options.output; + } + + //Allow for a json string of json object + obj = typeof obj === "string" ? JSON.parse(obj) : obj; + + //Render if we have a correct template & data object + if(typeof(template) === "object" && template !== null && typeof(obj) === "object" && obj !== null) out = _render(obj, template, _options); + + //Determine what output we need + switch(_options.output) { + + case "ihtml": + return(out); + break; + + //Default to html + default: + return(out.html); + break; + } + }, + + //Extend json2html with custom template types + // eg <> = html element, or [] = component + // method = function(obj, template, index, options) + // MUST return an iHTML object + "extend":function(type,method){ + + //Add the custom template type + TYPES[type] = method; + }, + + //json2html component methods + "component":{ + + //Add a component (name = string, template = json2html template) + //OR function(components) where component is obj with name:template property eg {"name":template,...} + "add":function(name,template){ + + //Determine what we're adding + switch(typeof(name)) { + + //Multiple components + case "object": + + //Components + COMPONENTS = _extend(COMPONENTS,name); + break; + + //One component + case "string": + COMPONENTS[name] = template; + break; + + //Not supported + default: + break; + } + }, + + //Get a component + "get":function(name) { + return(COMPONENTS[name]); + } + }, + + //Allow access to the iHTML object + "iHTML":iHTML, + + //Encode the html string to text + "toText":function(html) { + + //Check for undefined or null + if(html === undefined || html === null) return(""); + + //Otherwise convert to a string and encode HTML components + return html.toString() + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/\"/g, """) + .replace(/\'/g, "'") + .replace(/\//g, "/"); + } + }; + + //DEPRECATED (use json2html.render instead) + root.json2html.transform = root.json2html.render; + + /* ---------------------------------------- jQuery Plugin ------------------------------------------------ */ + + //If jQuery is defined add plugin + if(typeof(window) === "object") + if(window.jQuery) { + (function($){ + + /* ---------------------------------------- Public Methods ------------------------------------------------ */ + + //jQuery render template + // obj : json object to render or json string + // template: json2html template (array or json object) + // options : {} + // components : {name:template,...} + // data: object passed to jquery events + // output : ihtml | html + $.json2html = function(obj, template, options) { + + //Make sure we have the json2html base loaded + if(typeof json2html === "undefined") return(undefined); + + //Default options + var _options = { + "output":"ihtml" + }; + + //Parse the user defined options + if(options) { + + // LEGACY support for eventData, now data + if(options.eventData) _options.data = _options.eventData; + + //Add the other allowed options + _options.components = options.components; + _options.data = options.data; + + //Make sure we don't overwrite the default value + if(options.output) _options.output = options.output; + } + + //Determine what type of object we want as the output + switch(_options.output){ + + //LEGACY + case "json2html": + + //iHTML object + case "ihtml": + + //set the output as ihtml + _options.output = "ihtml"; + break; + + //Return raw html (same as calling json2html.render) + default: + break; + } + + return(json2html.render(obj, template, _options)); + }; + + //jQuery render template via chaining + // obj : json object to render or json string + // template: json2html template (array or json object) + // options : {} + // components : {name:template,...} + // method: "append" | "replace" | "prepend" + // data: object passed to jquery events + $.fn.json2html = function(obj, template, options) { + + //Make sure we have the json2html base loaded + if(typeof json2html === "undefined") return(undefined); + + var _options = { + + //Always set the output to ihtml + "output":"ihtml" + }; + + //Parse the user defined options + if(options) { + + // LEGACY support for eventData, now data + if(options.eventData) _options.data = _options.eventData; + + // LEGACY support for append,prepend,replace, now method + if(options.prepend) _options.method = "prepend"; + if(options.replace) _options.method = "replace"; + if(options.append) _options.method = "append"; + + //Add the other allowed options + _options.components = options.components; + _options.data = options.data; + } + + //Render each object + return this.each(function(){ + + //Render the template and attach to the dom + var dom = _dom(json2html.render(obj, template, _options)); + + //Determine how we should add the new content + switch(_options.method) { + + //Replace + case "replace": + $.fn.replaceWith.call($(this),dom.parent); + break; + + //Prepend + case "prepend": + $.fn.prepend.call($(this),dom.parent); + break; + + //Default to append + default: + $.fn.append.call($(this),dom.parent); + break; + } + + //Throw the json2html.ready events (if any) + _onready(dom.ready); + }); + }; + + //Hydrate the json2html elements with these events + $.fn.j2hHydrate = function(events) { + + //Attach the events for each element + return this.each(function(){ + + //Attach the events and trigger the onready for this element + _onready( _attachEvents($(this),events) ); + }); + }; + + /* ---------------------------------------- Prviate Methods ------------------------------------------------ */ + + //Trigger the on ready events + function _onready(events){ + + //Trigger all the json2html.ready events + for(var i=0; i < events.length; i++) + events[i].trigger("j2h.ready"); + } + + //Add the ihtml object to the dom + // returns the parent and ready event object + function _dom(ihtml) { + + //Attach the html(string) + var parent = $(document.createElement("i")).html(ihtml.html); + + //Attach the events to the parent object in the dom + var ready = _attachEvents($(parent),ihtml.events); + + //Get the children to this result + return({"parent":$(parent).children(),"ready":ready}); + } + + //Attach the events to the children of this element + function _attachEvents($parent,events) { + + //Record json2html specific ready events + var ready = []; + + //Determine if we have events + for(var i = 0; i < events.length; i++) { + + var event = events[i]; + + //find the associated DOM object with this event + var obj = $parent.find("[-j2h-e='" + event.id + "']"); + + //Check to see if we found this element or not + if(obj.length === 0) throw "jquery.json2html was unable to attach event " + event.id + " to DOM"; + + //remove the attribute + $(obj).removeAttr("-j2h-e"); + + //Check for the ready event + // jquery ready event only works for document + // we extend that to work for any dom element + // replace with json2html.ready + if(event.type === "ready") event.type = "j2h.ready"; + + //Add the action to the data object + event.data.action = event.action; + + //attach the events to the dom + $(obj).on(event.type,event.data,function(e){ + + //attach the jquery event + e.data.event = e; + + //call the appropriate method + if(_typeof(e.data.action) === "function") e.data.action.call($(this),e.data); + }); + + //Add to ready event list (if needed) + if(event.type === "j2h.ready") ready.push($(obj)); + } + + //Return the ready events + return(ready); + } + })(window.jQuery); + } + + /* ---------------------------------------- Private Methods ------------------------------------------------ */ + + //Render object + // returns interactive html object (ihtml) + function _render(obj, template, options) { + + var ihtml = new iHTML(); + + //Check to see what type of object we're rending + switch(_typeof(obj,true)) { + + case "array": + + //Itterrate through the array and add it to the ihtml object + var len=obj.length; + for(var j=0;j 0 ) { + + //Get the sub object using the path + subObj = subObj[_path[i]]; + + //Break if we don't have this sub object + if(subObj === null || subObj === undefined) break; + } + } + + //Return an empty string if we don't have a value + if(subObj === null || subObj === undefined) return(""); + + return(subObj); + } + + //Get the html value of the object + function _getValue(obj, template, key, index, options) { + + var out = ""; + + //Get the template property + var prop = template[key]; + + //Check the type of this template property + switch(_typeof(prop)) { + + //Get the value from the function + case "function": + + //Check what typeof value is for the object we're rendering + switch(_typeof(obj)) { + + //If this is a json object or array then get the component that we want + case "object": + + //Otherwise get the value + return( prop.call(obj,obj,index,options.data) ); + break; + + //NOT SUPPORTED + case "function": + case "undefined": + case "null": + return(""); + break; + + //BOOLEAN, NUMBER, BIGINT, STRING, SYMBOL + default: + + //Create a new object with the properties (value & index) + var _obj = {"value":obj,"index":index,"data":options.data}; + return(prop.call(_obj,_obj,index,options.data)); + break; + } + break; + + //Check for short hand ${..} + case "string": + + //Parse the property string and fill in any tokens + out = _parse(prop,function(all,path){ + + //Check what typeof value is for the object we're rendering + switch(_typeof(obj)) { + + //If this is an json object then get the value we're looking for + case "object": + return(_get(obj,path)); + break; + + //NOT SUPPORTED + case "function": + case "undefined": + case "null": + return(""); + break; + + //BOOLEAN, NUMBER, BIGINT, STRING, SYMBOL + default: + + //Check the path of the shorthand + switch(path) { + + //RESERVED word for static array value + case "value": + return(obj); + break; + + //RESERVED word for static array value index + case "index": + return(index); + break; + } + break; + } + }); + break; + + //Spit out blank for undefined or null + case "null": + case "undefined": + out = ""; + break; + + default: + + //Get the string representation for this property + out = prop.toString(); + break; + } + + return(out); + } + + //Extend object + // (obj1,obj2,...) + // creates a new object with properties of all objects + // shallow copy only + function _extend(){ + + var out = {}; + + //Itterate over all objects and copy each property + // shallow copy only + var len = arguments.length; + for(var i=0; i < len;i++) + for (var prop in arguments[i]) { out[prop] = arguments[i][prop]; } + + return(out); + } + + //isArray (fix for IE prior to 9) + function _isArray(obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + } + + //Typeof helper + function _typeof(obj,checkArray) { + + var type = typeof obj; + + //Check what kind of object this is + if(type === "object") { + + //Check for null + if(obj === null) return("null"); + + //Check for array + if(checkArray) + if(_isArray(obj)) return("array"); + } + + return(type); + } + + //Get a new random id + function _id() { + var S4 = function() { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); + }; + return (S4()+S4()); + } + + //Determines if we have a void element + // (No end tag, and must not contain any contents) + function _isVoidElement(element) { + + //Determine if we match any of the void elements + // as specified by https://www.w3.org/TR/html5/syntax.html#void-elements + switch(element) { + + //Allow these void elements + case "area": + case "base": + case "br": + case "col": + case "command": + case "embed": + case "hr": + case "img": + case "input": + case "keygen": + case "link": + case "meta": + case "param": + case "source": + case "track": + case "wbr": + return(true); + break; + + //Otherwise we're not void + default: + return(false); + break; + } + } + + //Use the tokenizer to parse the str + function _parse(str, method) { + + let tokenizer = new Tokenizer([ + /\${([\w\.\,]+)}/ + ],function( src, real, re ){ + return real ? src.replace(re,method) : src; + } + ); + + return(tokenizer.parse(str).join("")); + } + + /* ---------------------------------------- Template Types ------------------------------------------------ */ + + //default html type + // supports <> and legacy tag type + // returns iHTML + function _html(type, obj, template, index, options){ + + //Create a new ihtml object for the parent + var parent = new iHTML(); + + //Get the element name (this can be tokenized) + var name = _getValue(obj,template,type,index,options); + + //Determine if this is a void element + // shouldn't have any contents, if it does then ignore + var isVoid = _isVoidElement(name); + + //Create a new element + parent.appendHTML("<" + name); + + //Create a new ihtml object for the children + var children = new iHTML(); + + //innerHTML + var html; + + //Look into the properties of this template + for (var prop in template) { + + switch(prop) { + + //DEPRECATED (use <> instead) + case "tag": + + //HTML element to render + case "<>": + //Do nothing as we have already created the element + break; + + //Encode as text + case "text": + + //Ignore for void elements + if(isVoid) continue; + + //Determine what kind of object this is + // array => NOT SUPPORTED + // other => text + if(!_isArray(template[prop])) { + //Get the encoded text associated with this element + html = json2html.toText( _getValue(obj,template,prop,index,options) ); + } + + break; + + //DEPRECATED (use HTML instead) + case "children": + + //Encode as HTML + // accepts array of children, functions, string, number, boolean + case "html": + + //Ignore for void elements + if(isVoid) continue; + + //Determine what kind of object this is + // array & function => children + // other => html + switch(_typeof(template[prop],true)) { + + case "array": + + //_apply the template to the children + children.append( _apply(obj, template[prop], index, options) ); + break; + + case "function": + + //Get the result from the function + var temp = template[prop].call(obj, obj, index, options.data, options.$html); + + //Determine what type of object this is + switch(_typeof(temp,true)) { + + //This is a template and we need to template it + case "array": + //Add this object to the children + children.append( _apply(obj, temp, index, options) ); + break; + + //Only returned by json2html.render or $.json2html calls + case "object": + //Add this object to the children + if(temp.type === "iHTML") children.append(temp); + break; + + //Not supported + case "function": + case "undefined": + case "null": + break; + + //Append to html + // string, number, boolean + default: + children.appendHTML(temp); + break; + } + break; + + default: + //Get the HTML associated with this element + html = _getValue(obj,template,prop,index,options); + break; + } + break; + + default: + //Add the property as a attribute if it's not a key one + var isEvent = false; + + //Check if the first two characters are 'on' then this is an event + if( prop.length > 2 ) + if( prop.substring(0,2).toLowerCase() === "on" ) { + + //Determine if we should add events + if(options.output === "ihtml") { + + //if so then setup the event data + var data = { + "obj":obj, + "data":options.data, + "index":index + }; + + //create a new id for this event + var id = _id(); + + //append the new event + parent.events.push( {"id":id,"type":prop.substring(2),"data":data,"action":template[prop]} ); + + //Insert temporary event property -j2h-e + parent.appendHTML(" -j2h-e='" + id + "'"); + } + + //this is an event + isEvent = true; + } + + //If this wasn't an event AND we actually have a value then add it as a property + if(!isEvent){ + //Get the value + var val = _getValue(obj, template, prop, index, options); + + //Make sure we have a value + if(val !== undefined) { + var out; + + //Determine the output type of this value (wrap with quotes) + if(typeof val === "string") out = '"' + val.replace(/"/g, '"') + '"'; + else out = val; + + //create the name value pair + parent.appendHTML(" " + prop + "=" + out); + } + } + break; + } + } + + //For non void elements + if(!isVoid) { + + //Close the opening tag + parent.appendHTML(">"); + + //add the innerHTML (if we have some) + if(html) parent.appendHTML(html); + + //add the children (if we have any) + parent.append(children); + + //add the closing tag + parent.appendHTML(""); + } else { + + //For void elements just close the opening tag + parent.appendHTML("/>"); + } + + return(parent); + } + + //component type + // supports [] + // returns iHTML + function _component(obj, template, index, options) { + + //Create a new ihtml object for the parent + var ihtml = new iHTML(); + + var component = { + "template":undefined, + "name":undefined + }; + + //Use the source object by default + var data = obj; + + for(var prop in template) { + + //Check the property + switch(prop) { + + //REQUIRED + case "[]": + + //Get the component name + var name = _getValue(obj, template, prop, index, options); + + //Check for a local component first + if(options.components) component.template = options.components[name]; + + //Otherwise check the global components (if we didn't have a local template) + if(!component.template) component.template = COMPONENTS[name]; + + //Otherwise ignore + break; + + //Embed this template within the component + // if needed + case "html": + + //Check what object type of template we allow + switch(_typeof(template.html,true)) { + + //Set the html embed + case "array": + options.$html = template.html; + break; + + //Make sure this is a template + // AND it's always in an array form + case "object": + options.$html = [template.html]; + break; + + default: + break; + } + + break; + + //OPTIONAL + //Custom data object + // MUST be a function + case "data": + + //Get the data (if required) + switch(_typeof(template.data)) { + + case "function": + + //Call the data function to get the data for this template + data = template.data.call(obj, obj, index, options.data); + break; + + //OTHERWISE NOT IMPLEMENTED + } + + break; + } + } + + //If we don't have the component then exit + // template can be an object or array + if(_typeof(component.template) !== "object") return(ihtml); + + //template and assign to the output + ihtml.append(_render(data, component.template, options)); + + return(ihtml); + } +}()); + + diff --git a/gemp-lotr/gemp-lotr-server/pom.xml b/gemp-lotr/gemp-lotr-server/pom.xml index 02fbd324a..0c5949577 100644 --- a/gemp-lotr/gemp-lotr-server/pom.xml +++ b/gemp-lotr/gemp-lotr-server/pom.xml @@ -73,5 +73,15 @@ 1.9.0 test + + com.google.code.gson + gson + 2.8.2 + + + org.sql2o + sql2o + 1.6.0 + \ No newline at end of file diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/DbGameHistoryDAO.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/DbGameHistoryDAO.java index 4b2618f33..d52038f3b 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/DbGameHistoryDAO.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/DbGameHistoryDAO.java @@ -2,6 +2,7 @@ package com.gempukku.lotro.db; import com.gempukku.lotro.db.vo.GameHistoryEntry; import com.gempukku.lotro.game.Player; +import org.sql2o.Sql2o; import java.sql.Connection; import java.sql.PreparedStatement; @@ -42,38 +43,47 @@ public class DbGameHistoryDAO implements GameHistoryDAO { } public List getGameHistoryForPlayer(Player player, int start, int count) { - try { - try (Connection connection = _dbAccess.getDataSource().getConnection()) { - try (PreparedStatement statement = connection.prepareStatement("select winner, loser, win_reason, lose_reason, win_recording_id, lose_recording_id, format_name, tournament, winner_deck_name, loser_deck_name, start_date, end_date from game_history where winner=? or loser=? order by end_date desc limit ?, ?")) { - statement.setString(1, player.getName()); - statement.setString(2, player.getName()); - statement.setInt(3, start); - statement.setInt(4, count); - try (ResultSet rs = statement.executeQuery()) { - List result = new LinkedList(); - while (rs.next()) { - String winner = rs.getString(1); - String loser = rs.getString(2); - String winReason = rs.getString(3); - String loseReason = rs.getString(4); - String winRecordingId = rs.getString(5); - String loseRecordingId = rs.getString(6); - String formatName = rs.getString(7); - String tournament = rs.getString(8); - String winnerDeckName = rs.getString(9); - String loserDeckName = rs.getString(10); - Date startDate = new Date(rs.getLong(11)); - Date endDate = new Date(rs.getLong(12)); - GameHistoryEntry entry = new GameHistoryEntry(winner, winReason, winRecordingId, loser, loseReason, loseRecordingId, formatName, tournament, winnerDeckName, loserDeckName, startDate, endDate); - result.add(entry); - } - return result; - } - } + try { + + Sql2o db = new Sql2o(_dbAccess.getDataSource()); + + try (org.sql2o.Connection conn = db.open()) { + String sql = "select winner, loser, win_reason, lose_reason, win_recording_id, lose_recording_id, format_name, tournament, winner_deck_name, loser_deck_name, start_date, end_date from game_history where winner=:playerName or loser=:playerName order by end_date desc limit :start, :count"; + List result = conn.createQuery(sql) + .addParameter("playerName", player.getName()) + .addParameter("start", start) + .addParameter("count", count) + .executeAndFetch(GameHistoryEntry.class); + + return result; } - } catch (SQLException exp) { - throw new RuntimeException("Unable to get count of player games", exp); + } catch (Exception ex) { + throw new RuntimeException("Unable to retrieve game history for player", ex); + } + + } + + @Override + public List getGameHistoryForFormat(String format, int count) { + try { + + Sql2o db = new Sql2o(_dbAccess.getDataSource()); + + try (org.sql2o.Connection conn = db.open()) { + String sql = "SELECT winner, loser, win_reason, lose_reason, win_recording_id, lose_recording_id, format_name, tournament, winner_deck_name, loser_deck_name, start_date, end_date " + + " FROM game_history " + + " WHERE format_name LIKE :format" + + " ORDER BY end_date DESC LIMIT :count"; + List result = conn.createQuery(sql) + .addParameter("format", "%" + format + "%") + .addParameter("count", count) + .executeAndFetch(GameHistoryEntry.class); + + return result; + } + } catch (Exception ex) { + throw new RuntimeException("Unable to retrieve game history by format", ex); } } @@ -182,39 +192,24 @@ public class DbGameHistoryDAO implements GameHistoryDAO { @Override public List getLastGames(String requestedFormatName, int count) { - try { - try (Connection connection = _dbAccess.getDataSource().getConnection()) { - try (PreparedStatement statement = connection.prepareStatement( - "select winner, loser, win_reason, lose_reason, win_recording_id, lose_recording_id, format_name, " + - "tournament, winner_deck_name, loser_deck_name, start_date, end_date from game_history " + - "where format_name=? order by end_date desc limit ?")) { - statement.setString(1, requestedFormatName); - statement.setInt(2, count); - try (ResultSet rs = statement.executeQuery()) { - List result = new LinkedList(); - while (rs.next()) { - String winner = rs.getString(1); - String loser = rs.getString(2); - String winReason = rs.getString(3); - String loseReason = rs.getString(4); - String winRecordingId = rs.getString(5); - String loseRecordingId = rs.getString(6); - String formatName = rs.getString(7); - String tournament = rs.getString(8); - String winnerDeckName = rs.getString(9); - String loserDeckName = rs.getString(10); - Date startDate = new Date(rs.getLong(11)); - Date endDate = new Date(rs.getLong(12)); - GameHistoryEntry entry = new GameHistoryEntry(winner, winReason, winRecordingId, loser, loseReason, loseRecordingId, formatName, tournament, winnerDeckName, loserDeckName, startDate, endDate); - result.add(entry); - } - return result; - } - } + try { + + Sql2o db = new Sql2o(_dbAccess.getDataSource()); + + try (org.sql2o.Connection conn = db.open()) { + String sql = "select winner, loser, win_reason, lose_reason, win_recording_id, lose_recording_id, format_name, " + + "tournament, winner_deck_name, loser_deck_name, start_date, end_date from game_history " + + "where format_name=:formatName order by end_date desc limit :count"; + List result = conn.createQuery(sql) + .addParameter("formatName", requestedFormatName) + .addParameter("count", count) + .executeAndFetch(GameHistoryEntry.class); + + return result; } - } catch (SQLException exp) { - throw new RuntimeException("Unable to get list of games", exp); + } catch (Exception ex) { + throw new RuntimeException("Unable to retrieve last games", ex); } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/GameHistoryDAO.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/GameHistoryDAO.java index 1ba622d6d..94e85d354 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/GameHistoryDAO.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/GameHistoryDAO.java @@ -14,6 +14,8 @@ public interface GameHistoryDAO { public int getGameHistoryForPlayerCount(Player player); + public List getGameHistoryForFormat(String format, int count); + public int getActivePlayersCount(long from, long duration); public int getGamesPlayedCount(long from, long duration); diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/vo/GameHistoryEntry.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/vo/GameHistoryEntry.java index 92521ef74..9a2bf83e3 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/vo/GameHistoryEntry.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/vo/GameHistoryEntry.java @@ -3,83 +3,38 @@ package com.gempukku.lotro.db.vo; import java.util.Date; public class GameHistoryEntry { - private String _winner; - private String _loser; - private String _winReason; - private String _loseReason; + public int id; - private String _winnerRecording; - private String _loserRecording; + public String winner; + public String loser; - private String _formatName; - private String _tournament; - private String _winnerDeckName; - private String _loserDeckName; + public String win_reason; + public String lose_reason; - private Date _startTime; - private Date _endTime; + public String win_recording_id; + public String lose_recording_id; - public GameHistoryEntry(String winner, String winReason, String winnerRecording, String loser, String loseReason, String loserRecording, String formatName, String tournament, String winnerDeckName, String loserDeckName, Date startTime, Date endTime) { - _winner = winner; - _winReason = winReason; - _winnerRecording = winnerRecording; - _loser = loser; - _loseReason = loseReason; - _loserRecording = loserRecording; - _formatName = formatName; - _tournament = tournament; - _winnerDeckName = winnerDeckName; - _loserDeckName = loserDeckName; - _startTime = startTime; - _endTime = endTime; + public long start_date; + public long end_date; + + public String format_name; + + public String winner_deck_name; + public String loser_deck_name; + + public String tournament; + + + public Date GetStartDate() + { + return new Date(start_date); } - public String getLoser() { - return _loser; + public Date GetEndDate() + { + return new Date(end_date); } - public String getLoseReason() { - return _loseReason; - } - public String getLoserRecording() { - return _loserRecording; - } - - public String getWinner() { - return _winner; - } - - public String getWinnerRecording() { - return _winnerRecording; - } - - public String getWinReason() { - return _winReason; - } - - public String getFormatName() { - return _formatName; - } - - public String getTournament() { - return _tournament; - } - - public String getWinnerDeckName() { - return _winnerDeckName; - } - - public String getLoserDeckName() { - return _loserDeckName; - } - - public Date getEndTime() { - return _endTime; - } - - public Date getStartTime() { - return _startTime; - } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameHistoryService.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameHistoryService.java index 86b4d999c..025e0b8db 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameHistoryService.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameHistoryService.java @@ -40,6 +40,10 @@ public class GameHistoryService { return _gameHistoryDAO.getGameHistoryForPlayer(player, start, count); } + public List getGameHistoryForFormat(String format, int count) { + return _gameHistoryDAO.getGameHistoryForFormat(format, count); + } + public List getTrackableGames(int count) { return _gameHistoryDAO.getLastGames("Second Edition", count); }