Playtesting tab

Adding a new playtesting tab that explains the PC playtesting initiative.  A new 't' flag is now part of the user type as a result.  The three PC Playtesting formats will not show unless the user opts in.
This commit is contained in:
Christian 'ketura' McCarty
2021-03-22 01:47:24 -05:00
parent 0d9209a83d
commit edfa007ad0
13 changed files with 243 additions and 6 deletions

View File

@@ -83,8 +83,13 @@ public class GempukkuHttpRequestHandler extends SimpleChannelInboundHandler<Full
if (uri.indexOf("?") > -1)
uri = uri.substring(0, uri.indexOf("?"));
String ip = httpRequest.headers().get("X-Forwarded-For");
if(ip == null)
ip = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
final RequestInformation requestInformation = new RequestInformation(httpRequest.getUri(),
httpRequest.headers().get("X-Forwarded-For"),
ip,
System.currentTimeMillis());
ResponseSender responseSender = new ResponseSender(ctx, httpRequest);

View File

@@ -286,11 +286,17 @@ public class HallRequestHandler extends LotroServerRequestHandler implements Uri
Document doc = documentBuilder.newDocument();
Player player = getResourceOwnerSafely(request, null);
Element hall = doc.createElement("hall");
hall.setAttribute("currency", String.valueOf(_collectionManager.getPlayerCollection(resourceOwner, CollectionType.MY_CARDS.getCode()).getCurrency()));
_hallServer.signupUserForHall(resourceOwner, new SerializeHallInfoVisitor(doc, hall));
for (Map.Entry<String, LotroFormat> format : _formatLibrary.getHallFormats().entrySet()) {
//playtest formats are opt-in
if (format.getKey().startsWith("test") && !player.getType().contains("t"))
continue;
Element formatElem = doc.createElement("format");
formatElem.setAttribute("type", format.getKey());
formatElem.appendChild(doc.createTextNode(format.getValue().getName()));

View File

@@ -0,0 +1,105 @@
package com.gempukku.lotro.async.handler;
import com.gempukku.lotro.DateUtils;
import com.gempukku.lotro.async.HttpProcessingException;
import com.gempukku.lotro.async.ResponseWriter;
import com.gempukku.lotro.cache.CacheManager;
import com.gempukku.lotro.collection.CollectionsManager;
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.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.formats.LotroFormatLibrary;
import com.gempukku.lotro.hall.HallServer;
import com.gempukku.lotro.league.*;
import com.gempukku.lotro.service.AdminService;
import com.gempukku.lotro.tournament.TournamentService;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
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 javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.*;
public class PlaytestRequestHandler extends LotroServerRequestHandler implements UriRequestHandler {
private PlayerDAO _playerDAO;
public PlaytestRequestHandler(Map<Type, Object> context) {
super(context);
_playerDAO = extractObject(context, PlayerDAO.class);
}
@Override
public void handleRequest(String uri, HttpRequest request, Map<Type, Object> context, ResponseWriter responseWriter, String remoteIp) throws Exception {
if (uri.equals("/addTesterFlag") && request.getMethod() == HttpMethod.POST) {
addTesterFlag(request, responseWriter);
} else if (uri.equals("/removeTesterFlag") && request.getMethod() == HttpMethod.POST) {
removeTesterFlag(request, responseWriter);
} else if (uri.equals("/getTesterFlag") && request.getMethod() == HttpMethod.GET) {
getTesterFlag(request, responseWriter);
} else {
throw new HttpProcessingException(404);
}
}
private void addTesterFlag(HttpRequest request, ResponseWriter responseWriter) throws Exception {
HttpPostRequestDecoder postDecoder = new HttpPostRequestDecoder(request);
try {
Player player = getResourceOwnerSafely(request, null);
_playerDAO.addPlayerFlag(player.getName(), "t");
responseWriter.writeHtmlResponse("OK");
} finally {
postDecoder.destroy();
}
}
private void removeTesterFlag(HttpRequest request, ResponseWriter responseWriter) throws Exception {
HttpPostRequestDecoder postDecoder = new HttpPostRequestDecoder(request);
try {
Player player = getResourceOwnerSafely(request, null);
_playerDAO.removePlayerFlag(player.getName(), "t");
responseWriter.writeHtmlResponse("OK");
} finally {
postDecoder.destroy();
}
}
private void getTesterFlag(HttpRequest request, ResponseWriter responseWriter) throws Exception {
HttpPostRequestDecoder postDecoder = new HttpPostRequestDecoder(request);
try {
Player player = getResourceOwnerSafely(request, null);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
Element hasTester = doc.createElement("hasTester");
hasTester.setAttribute("result", String.valueOf(player.getType().contains("t")));
responseWriter.writeXmlResponse(doc);
} finally {
postDecoder.destroy();
}
}
}

View File

@@ -33,6 +33,7 @@ public class RootUriRequestHandler implements UriRequestHandler {
private PlayerStatsRequestHandler _playerStatsRequestHandler;
private TournamentRequestHandler _tournamentRequestHandler;
private SoloDraftRequestHandler _soloDraftRequestHandler;
private PlaytestRequestHandler _playtestRequestHandler;
private Pattern originPattern;
@@ -58,6 +59,7 @@ public class RootUriRequestHandler implements UriRequestHandler {
_playerStatsRequestHandler = new PlayerStatsRequestHandler(context);
_tournamentRequestHandler = new TournamentRequestHandler(context);
_soloDraftRequestHandler = new SoloDraftRequestHandler(context);
_playtestRequestHandler = new PlaytestRequestHandler(context);
}
@Override
@@ -110,6 +112,10 @@ public class RootUriRequestHandler implements UriRequestHandler {
_tournamentRequestHandler.handleRequest(uri.substring(_serverContextPath.length() + 10), request, context, responseWriter, remoteIp);
} else if (uri.startsWith(_serverContextPath + "soloDraft")) {
_soloDraftRequestHandler.handleRequest(uri.substring(_serverContextPath.length() + 9), request, context, responseWriter, remoteIp);
} else if (uri.startsWith(_serverContextPath + "playtesting")) {
_playtestRequestHandler.handleRequest(uri.substring(_serverContextPath.length() + 11), request, context, responseWriter, remoteIp);
} else {
throw new HttpProcessingException(404);
}

View File

@@ -45,6 +45,12 @@
<audio id="gamestart" src="/gemp-lotr/fanfare_x.mp3" type="audio/mpeg" preload="metadata"></audio>
<style type="text/css">
.highlight-new {
background-color: #aa2222 !important;
}
</style>
<script type="text/javascript">
var chat;
var hall;
@@ -168,6 +174,7 @@
<li><a href="includes/contribute.html">Contribute</a></li>
<li><a href="includes/yourStats.html">Your stats</a></li>
<li><a href="includes/stats.html">Server stats</a></li>
<li ><a class="highlight-new" href="includes/playtesting.html">Playtesting (new!)</a></li>
<script type="text/javascript">
document.write('<li><a href="includes/changeLog.html?_=' + (new Date().getTime()) + '">Change Log</a></li>');
</script>

View File

@@ -0,0 +1,16 @@
<div style="width:max(800px, 50%); margin:auto; font-size: 125%">
<p>The <a href="https://lotrtcgpc.net">Player's Council</a> is dedicated to cleaning up the game's fossilized meta, and we aim to do so both through errata of broken (or underperforming) cards and the introduction of new cards compatible with existing formats.</p>
<p>Although we spend weeks or months arguing the finer points of card balancing, there is no argument as powerful as the result of dedicated playtesting. Once a card has been developed to the point of requiring playtesting, we release it here on Gemp and unlock it within special hidden playtesting formats.</p>
<p>If you would like to help us do this by playing matches with these cards, then use the button below to enable the playtesting formats on your account! If later you decide you no longer want to see how the sausage is made, then return to this screen and click the disable button instead.</p>
<button id="btnEnableTesting" onclick="hall.AddTesterFlag()">Enable Playtesting</button>
<button id="btnDisableTesting" onclick="hall.RemoveTesterFlag()">Disable Playtesting</button>
<p>After playing playtest games, you can leave feedback <a href="https://docs.google.com/forms/d/e/1FAIpQLSdKJrCmjoyUqDTusDcpNoWAmvkGdzQqTxWGpdNIFX9biCee-A/viewform?usp=pp_url">using this form here</a>.</p>
<p>If you have any questions or would like to discuss our card design decisions in-depth, then feel free to join us <a href="https://lotrtcgpc.net/discord">on the PC Discord server</a>.</p>
</div>

View File

@@ -40,7 +40,7 @@
margin-right: auto;
}
.status {
.status, .error {
margin: 0;
margin-left: auto;
margin-right: auto;

View File

@@ -615,6 +615,33 @@ var GempLotrCommunication = Class.extend({
dataType:"html"
});
},
addTesterFlag:function (callback, errorMap) {
$.ajax({
type:"POST",
url:this.url + "/playtesting/addTesterFlag",
cache:false,
data:{
participantId:getUrlParam("participantId")},
success:this.deliveryCheck(callback),
error:this.errorCheck(errorMap),
dataType:"html"
});
},
removeTesterFlag:function (callback, errorMap) {
$.ajax({
type:"POST",
url:this.url + "/playtesting/removeTesterFlag",
cache:false,
data:{
participantId:getUrlParam("participantId")},
success:this.deliveryCheck(callback),
error:this.errorCheck(errorMap),
dataType:"html"
});
},
getStatus:function (callback, errorMap) {
$.ajax({
type:"GET",

View File

@@ -402,6 +402,22 @@ var GempLotrHallUI = Class.extend({
var myAudio = document.getElementById(soundObj);
myAudio.play();
},
AddTesterFlag: function() {
var that = this;
that.comm.addTesterFlag(function () {
window.location.reload(true);
});
},
RemoveTesterFlag: function() {
var that = this;
that.comm.removeTesterFlag(function () {
window.location.reload(true);
});
},
processHall:function (xml) {
var that = this;
@@ -698,7 +714,10 @@ var GempLotrHallUI = Class.extend({
for (var i = 0; i < formats.length; i++) {
var format = formats[i].childNodes[0].nodeValue;
var type = formats[i].getAttribute("type");
this.supportedFormatsSelect.append("<option value='" + type + "'>" + format + "</option>");
var item = "<option value='" + type + "'>" + format + "</option>"
this.supportedFormatsSelect.append(item);
}
this.supportedFormatsInitialized = true;
}

View File

@@ -53,6 +53,22 @@ public class CachedPlayerDAO implements PlayerDAO, Cached {
return success;
}
@Override
public boolean addPlayerFlag(String login, String flag) throws SQLException {
final boolean success = _delegate.addPlayerFlag(login, flag);
if (success)
clearCache();
return success;
}
@Override
public boolean removePlayerFlag(String login, String flag) throws SQLException {
final boolean success = _delegate.removePlayerFlag(login, flag);
if (success)
clearCache();
return success;
}
@Override
public List<Player> findSimilarAccounts(String login) throws SQLException {
return _delegate.findSimilarAccounts(login);

View File

@@ -106,6 +106,30 @@ public class DbPlayerDAO implements PlayerDAO {
}
}
@Override
public boolean addPlayerFlag(String login, String flag) throws SQLException {
try (Connection conn = _dbAccess.getDataSource().getConnection()) {
try (PreparedStatement statement = conn.prepareStatement("UPDATE player SET type = CONCAT(type, ?) WHERE name=? AND type NOT LIKE CONCAT('%', ?, '%');")) {
statement.setString(1, flag);
statement.setString(2, login);
statement.setString(3, flag);
return statement.executeUpdate() == 1;
}
}
}
@Override
public boolean removePlayerFlag(String login, String flag) throws SQLException {
try (Connection conn = _dbAccess.getDataSource().getConnection()) {
try (PreparedStatement statement = conn.prepareStatement("UPDATE player SET type = REPLACE(type, ?, '') WHERE name=? AND type LIKE CONCAT('%', ?, '%');")) {
statement.setString(1, flag);
statement.setString(2, login);
statement.setString(3, flag);
return statement.executeUpdate() == 1;
}
}
}
@Override
public Player loginUser(String login, String password) throws SQLException {
try (Connection conn = _dbAccess.getDataSource().getConnection()) {

View File

@@ -16,6 +16,9 @@ public interface PlayerDAO {
public boolean unBanPlayer(String login) throws SQLException;
public boolean addPlayerFlag(String login, String flag) throws SQLException;
public boolean removePlayerFlag(String login, String flag) throws SQLException;
public List<Player> findSimilarAccounts(String login) throws SQLException;
public Player loginUser(String login, String password) throws SQLException;

View File

@@ -429,27 +429,30 @@
},
{
"name":"PLAYTEST - PC-FOTR",
"name":"PLAYTEST - PC Fellowship Block",
"code":"test_pc_fotr_block",
"sites":"FELLOWSHIP",
"cancelRingBearerSkirmish":true,
"hall":true,
"playtest":true,
"set":[1, 2, 3, 51, 52, 53, 73, 74, 75, 101, 151],
"banned":["1_40","1_311","2_32","3_42","3_68",]
},
{
"name":"PLAYTEST - PC-Movie",
"name":"PLAYTEST - PC Movie Block",
"code":"test_pc_movie",
"sites":"KING",
"hall":true,
"playtest":true,
"set":[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 51, 52, 53, 73, 74, 75, 101, 151],
"banned":["1_40","1_311","2_32","3_42","3_68", "1_45", "1_80", "1_108", "1_139", "1_195", "1_234", "1_248", "1_313", "3_106", "3_108", "3_38", "3_67", "7_49", "7_96", "8_1", "10_2", "10_91"],
},
{
"name":"PLAYTEST - PC-Expanded",
"name":"PLAYTEST - PC Expanded",
"code":"test_pc_expanded",
"sites":"SHADOWS",
"hall":true,
"playtest":true,
"banned":["1_40","1_311","2_32","3_42","3_68","1_45", "1_138", "1_234", "1_313", "1_316", "2_121", "3_17", "3_38", "3_67", "3_108", "3_113", "4_73", "7_49", "8_1", "10_2", "10_11", "10_91", "11_31", "11_100", "11_132"],
"restricted":["1_80", "1_108", "1_139", "1_195", "1_248", "2_75", "3_106", "4_276", "4_304"],
"set":[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 51, 52, 53, 73, 74, 75, 101, 151]